@loopstack/secrets-examples 0.1.1
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 +148 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/secrets-examples.module.d.ts +3 -0
- package/dist/secrets-examples.module.d.ts.map +1 -0
- package/dist/secrets-examples.module.js +30 -0
- package/dist/secrets-examples.module.js.map +1 -0
- package/dist/workflows/agentic/agentic-example.ui.yaml +6 -0
- package/dist/workflows/agentic/agentic-example.workflow.d.ts +29 -0
- package/dist/workflows/agentic/agentic-example.workflow.d.ts.map +1 -0
- package/dist/workflows/agentic/agentic-example.workflow.js +135 -0
- package/dist/workflows/agentic/agentic-example.workflow.js.map +1 -0
- package/dist/workflows/agentic/templates/system.md +7 -0
- package/dist/workflows/agentic/templates/systemMessage.md +3 -0
- package/dist/workflows/deterministic/deterministic-example.workflow.d.ts +18 -0
- package/dist/workflows/deterministic/deterministic-example.workflow.d.ts.map +1 -0
- package/dist/workflows/deterministic/deterministic-example.workflow.js +71 -0
- package/dist/workflows/deterministic/deterministic-example.workflow.js.map +1 -0
- package/dist/workflows/deterministic/templates/secretsVerified.md +8 -0
- package/package.json +48 -0
- package/src/index.ts +3 -0
- package/src/secrets-examples.module.ts +17 -0
- package/src/workflows/agentic/agentic-example.ui.yaml +6 -0
- package/src/workflows/agentic/agentic-example.workflow.ts +100 -0
- package/src/workflows/agentic/templates/system.md +7 -0
- package/src/workflows/agentic/templates/systemMessage.md +3 -0
- package/src/workflows/deterministic/deterministic-example.workflow.ts +47 -0
- package/src/workflows/deterministic/templates/secretsVerified.md +8 -0
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Secrets Examples
|
|
3
|
+
description: Workflow examples for secrets management in Loopstack — deterministic request/verify flow and an agentic LLM flow using get_secret_keys and request_secrets_task tools
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @loopstack/secrets-examples
|
|
7
|
+
|
|
8
|
+
> Secrets workflow examples for the [Loopstack](https://loopstack.ai) automation framework.
|
|
9
|
+
|
|
10
|
+
Two ways to manage workspace secrets: a scripted request/verify flow, and an agent loop where the LLM decides when to ask the user for credentials.
|
|
11
|
+
|
|
12
|
+
## Install as Source (Recommended)
|
|
13
|
+
|
|
14
|
+
Examples are meant to be read, copied, and adapted. Pull the source straight into your project with [giget](https://github.com/unjs/giget):
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx giget@latest gh:loopstack-ai/loopstack/registry/examples/secrets-examples src/secrets-examples
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
After copying, register the module in your app:
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { Module } from '@nestjs/common';
|
|
24
|
+
import { LoopstackModule } from '@loopstack/loopstack-module';
|
|
25
|
+
import { SecretsExamplesModule } from './secrets-examples/secrets-examples.module';
|
|
26
|
+
|
|
27
|
+
@Module({
|
|
28
|
+
imports: [LoopstackModule.forRoot(), SecretsExamplesModule],
|
|
29
|
+
})
|
|
30
|
+
export class AppModule {}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Install as a Dependency
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install @loopstack/secrets-examples
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { SecretsExamplesModule } from '@loopstack/secrets-examples';
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Environment
|
|
44
|
+
|
|
45
|
+
The agentic example requires Claude credentials:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Examples
|
|
52
|
+
|
|
53
|
+
| Example | Studio title | Description |
|
|
54
|
+
| ------------------------------- | --------------------------------- | -------------------------------------------------------------------- |
|
|
55
|
+
| [Deterministic](#deterministic) | `Secrets - Deterministic Example` | Scripted request/verify flow — no LLM involved |
|
|
56
|
+
| [Agentic](#agentic) | `Secrets - Agentic Example` | LLM agent that checks, requests, and verifies secrets via tool calls |
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Deterministic
|
|
61
|
+
|
|
62
|
+
A scripted workflow that requests two secrets from the user, waits for them to be stored, then verifies they were saved. No LLM involved.
|
|
63
|
+
|
|
64
|
+
### What it demonstrates
|
|
65
|
+
|
|
66
|
+
- Calling `RequestSecretsTool` with a list of expected keys
|
|
67
|
+
- Persisting a `SecretRequestDocument` so the user sees the prompt in Studio
|
|
68
|
+
- A `wait: true` transition that resumes once the user submits the secrets
|
|
69
|
+
- Reading current key availability via `GetSecretKeysTool`
|
|
70
|
+
|
|
71
|
+
### Key code
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
@Transition({ to: 'requesting_secrets' })
|
|
75
|
+
async requestSecretsFromUser() {
|
|
76
|
+
await this.requestSecrets.call({
|
|
77
|
+
variables: [{ key: 'EXAMPLE_API_KEY' }, { key: 'EXAMPLE_SECRET' }],
|
|
78
|
+
});
|
|
79
|
+
await this.documentStore.save(SecretRequestDocument, {
|
|
80
|
+
variables: [{ key: 'EXAMPLE_API_KEY' }, { key: 'EXAMPLE_SECRET' }],
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@Transition({ from: 'requesting_secrets', to: 'verifying', wait: true })
|
|
85
|
+
async secretsSubmitted() {
|
|
86
|
+
const result = await this.getSecretKeys.call();
|
|
87
|
+
this.assignState({ secretKeys: result.data });
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Files
|
|
92
|
+
|
|
93
|
+
- `deterministic-example.workflow.ts` — workflow class
|
|
94
|
+
- `templates/secretsVerified.md` — Handlebars summary template
|
|
95
|
+
|
|
96
|
+
## Agentic
|
|
97
|
+
|
|
98
|
+
An agent workflow where the LLM decides when to check for existing secrets and when to request new ones. The user can also send follow-up messages mid-loop.
|
|
99
|
+
|
|
100
|
+
### What it demonstrates
|
|
101
|
+
|
|
102
|
+
- Configuring an LLM call with allowed tools via `config: { tools: ['get_secret_keys', 'request_secrets_task'] }`
|
|
103
|
+
- A delegate/tool-result loop using `LlmDelegateToolCallsTool` + `LlmUpdateToolResultTool`
|
|
104
|
+
- Guard-based routing on `stopReason === 'tool_use'` vs end-of-turn
|
|
105
|
+
- A `prompt-input` widget that lets the user inject messages while the agent runs
|
|
106
|
+
|
|
107
|
+
### Key code
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
@Transition({ from: 'ready', to: 'prompt_executed' })
|
|
111
|
+
async llmTurn() {
|
|
112
|
+
const result = await this.llmGenerateText.call(
|
|
113
|
+
{},
|
|
114
|
+
{
|
|
115
|
+
config: {
|
|
116
|
+
provider: 'claude',
|
|
117
|
+
model: 'claude-haiku-4-5-20251001',
|
|
118
|
+
tools: ['get_secret_keys', 'request_secrets_task'],
|
|
119
|
+
system: this.render(join(__dirname, 'templates', 'system.md')),
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
this.assignState({ llmResult: result.data });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@Transition({ from: 'prompt_executed', to: 'awaiting_tools', priority: 10 })
|
|
127
|
+
@Guard('hasToolCalls')
|
|
128
|
+
async executeToolCalls(state) {
|
|
129
|
+
const result = await this.llmDelegateToolCalls.call({
|
|
130
|
+
message: state.llmResult!.message,
|
|
131
|
+
callback: { transition: 'toolResultReceived' },
|
|
132
|
+
});
|
|
133
|
+
this.assignState({ delegateResult: result.data });
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Files
|
|
138
|
+
|
|
139
|
+
- `agentic-example.workflow.ts` — workflow class
|
|
140
|
+
- `agentic-example.ui.yaml` — `prompt-input` widget for follow-up messages
|
|
141
|
+
- `templates/system.md` — system prompt
|
|
142
|
+
- `templates/systemMessage.md` — initial user/context message
|
|
143
|
+
|
|
144
|
+
## About
|
|
145
|
+
|
|
146
|
+
Author: [Jakob Klippel](https://www.linkedin.com/in/jakob-klippel/)
|
|
147
|
+
|
|
148
|
+
License: MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0DAA0D,CAAC;AACzE,cAAc,8CAA8C,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./secrets-examples.module"), exports);
|
|
18
|
+
__exportStar(require("./workflows/deterministic/deterministic-example.workflow"), exports);
|
|
19
|
+
__exportStar(require("./workflows/agentic/agentic-example.workflow"), exports);
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4DAA0C;AAC1C,2FAAyE;AACzE,+EAA6D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets-examples.module.d.ts","sourceRoot":"","sources":["../src/secrets-examples.module.ts"],"names":[],"mappings":"AAOA,qBASa,qBAAqB;CAAG"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.SecretsExamplesModule = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const claude_module_1 = require("@loopstack/claude-module");
|
|
12
|
+
const common_2 = require("@loopstack/common");
|
|
13
|
+
const secrets_module_1 = require("@loopstack/secrets-module");
|
|
14
|
+
const agentic_example_workflow_1 = require("./workflows/agentic/agentic-example.workflow");
|
|
15
|
+
const deterministic_example_workflow_1 = require("./workflows/deterministic/deterministic-example.workflow");
|
|
16
|
+
let SecretsExamplesModule = class SecretsExamplesModule {
|
|
17
|
+
};
|
|
18
|
+
exports.SecretsExamplesModule = SecretsExamplesModule;
|
|
19
|
+
exports.SecretsExamplesModule = SecretsExamplesModule = __decorate([
|
|
20
|
+
(0, common_2.StudioApp)({
|
|
21
|
+
title: 'Secrets Examples',
|
|
22
|
+
workflows: [deterministic_example_workflow_1.DeterministicExampleWorkflow, agentic_example_workflow_1.AgenticExampleWorkflow],
|
|
23
|
+
}),
|
|
24
|
+
(0, common_1.Module)({
|
|
25
|
+
imports: [claude_module_1.ClaudeModule, secrets_module_1.SecretsModule.forFeature()],
|
|
26
|
+
providers: [deterministic_example_workflow_1.DeterministicExampleWorkflow, agentic_example_workflow_1.AgenticExampleWorkflow],
|
|
27
|
+
exports: [deterministic_example_workflow_1.DeterministicExampleWorkflow, agentic_example_workflow_1.AgenticExampleWorkflow],
|
|
28
|
+
})
|
|
29
|
+
], SecretsExamplesModule);
|
|
30
|
+
//# sourceMappingURL=secrets-examples.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets-examples.module.js","sourceRoot":"","sources":["../src/secrets-examples.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,4DAAwD;AACxD,8CAA8C;AAC9C,8DAA0D;AAC1D,2FAAsF;AACtF,6GAAwG;AAWjG,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;CAAG,CAAA;AAAxB,sDAAqB;gCAArB,qBAAqB;IATjC,IAAA,kBAAS,EAAC;QACT,KAAK,EAAE,kBAAkB;QACzB,SAAS,EAAE,CAAC,6DAA4B,EAAE,iDAAsB,CAAC;KAClE,CAAC;IACD,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,4BAAY,EAAE,8BAAa,CAAC,UAAU,EAAE,CAAC;QACnD,SAAS,EAAE,CAAC,6DAA4B,EAAE,iDAAsB,CAAC;QACjE,OAAO,EAAE,CAAC,6DAA4B,EAAE,iDAAsB,CAAC;KAChE,CAAC;GACW,qBAAqB,CAAG"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { BaseWorkflow } from '@loopstack/common';
|
|
2
|
+
import type { TransitionInput } from '@loopstack/common';
|
|
3
|
+
import type { LlmDelegateResult, LlmGenerateTextResult } from '@loopstack/llm-provider-module';
|
|
4
|
+
import { LlmDelegateToolCallsTool, LlmGenerateTextTool, LlmUpdateToolResultTool } from '@loopstack/llm-provider-module';
|
|
5
|
+
import { GetSecretKeysTool, RequestSecretsTask, SecretsRequestWorkflow } from '@loopstack/secrets-module';
|
|
6
|
+
interface AgenticState {
|
|
7
|
+
llmResult?: LlmGenerateTextResult;
|
|
8
|
+
delegateResult?: LlmDelegateResult;
|
|
9
|
+
}
|
|
10
|
+
export declare class AgenticExampleWorkflow extends BaseWorkflow {
|
|
11
|
+
private readonly llmGenerateText;
|
|
12
|
+
private readonly llmDelegateToolCalls;
|
|
13
|
+
private readonly llmUpdateToolResult;
|
|
14
|
+
private readonly requestSecrets;
|
|
15
|
+
private readonly getSecretKeys;
|
|
16
|
+
private readonly secretsRequest;
|
|
17
|
+
constructor(llmGenerateText: LlmGenerateTextTool, llmDelegateToolCalls: LlmDelegateToolCallsTool, llmUpdateToolResult: LlmUpdateToolResultTool, requestSecrets: RequestSecretsTask, getSecretKeys: GetSecretKeysTool, secretsRequest: SecretsRequestWorkflow);
|
|
18
|
+
setup(_state: AgenticState): Promise<void>;
|
|
19
|
+
llmTurn(_state: AgenticState): Promise<void>;
|
|
20
|
+
executeToolCalls(state: AgenticState): Promise<void>;
|
|
21
|
+
hasToolCalls(state: AgenticState): boolean;
|
|
22
|
+
toolResultReceived(state: AgenticState, input: TransitionInput<Record<string, unknown>>): Promise<void>;
|
|
23
|
+
allToolsCompleteTransition(_state: AgenticState): void;
|
|
24
|
+
allToolsComplete(state: AgenticState): boolean;
|
|
25
|
+
userMessage(state: AgenticState, input: TransitionInput<string>): Promise<void>;
|
|
26
|
+
respond(_state: AgenticState): void;
|
|
27
|
+
}
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=agentic-example.workflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentic-example.workflow.d.ts","sourceRoot":"","sources":["../../../src/workflows/agentic/agentic-example.workflow.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAA+B,MAAM,mBAAmB,CAAC;AAC9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAC/F,OAAO,EAEL,wBAAwB,EACxB,mBAAmB,EAEnB,uBAAuB,EACxB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAE1G,UAAU,YAAY;IACpB,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC,cAAc,CAAC,EAAE,iBAAiB,CAAC;CACpC;AAED,qBAMa,sBAAuB,SAAQ,YAAY;IAEpD,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IACrC,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IACpC,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,cAAc;gBALd,eAAe,EAAE,mBAAmB,EACpC,oBAAoB,EAAE,wBAAwB,EAC9C,mBAAmB,EAAE,uBAAuB,EAC5C,cAAc,EAAE,kBAAkB,EAClC,aAAa,EAAE,iBAAiB,EAChC,cAAc,EAAE,sBAAsB;IAMnD,KAAK,CAAC,MAAM,EAAE,YAAY;IAQ1B,OAAO,CAAC,MAAM,EAAE,YAAY;IAiB5B,gBAAgB,CAAC,KAAK,EAAE,YAAY;IAQ1C,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO;IAKpC,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAU7F,0BAA0B,CAAC,MAAM,EAAE,YAAY;IAE/C,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO;IAKxC,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC;IAKrE,OAAO,CAAC,MAAM,EAAE,YAAY;CAC7B"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.AgenticExampleWorkflow = void 0;
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
const zod_1 = require("zod");
|
|
15
|
+
const common_1 = require("@loopstack/common");
|
|
16
|
+
const llm_provider_module_1 = require("@loopstack/llm-provider-module");
|
|
17
|
+
const secrets_module_1 = require("@loopstack/secrets-module");
|
|
18
|
+
let AgenticExampleWorkflow = class AgenticExampleWorkflow extends common_1.BaseWorkflow {
|
|
19
|
+
llmGenerateText;
|
|
20
|
+
llmDelegateToolCalls;
|
|
21
|
+
llmUpdateToolResult;
|
|
22
|
+
requestSecrets;
|
|
23
|
+
getSecretKeys;
|
|
24
|
+
secretsRequest;
|
|
25
|
+
constructor(llmGenerateText, llmDelegateToolCalls, llmUpdateToolResult, requestSecrets, getSecretKeys, secretsRequest) {
|
|
26
|
+
super();
|
|
27
|
+
this.llmGenerateText = llmGenerateText;
|
|
28
|
+
this.llmDelegateToolCalls = llmDelegateToolCalls;
|
|
29
|
+
this.llmUpdateToolResult = llmUpdateToolResult;
|
|
30
|
+
this.requestSecrets = requestSecrets;
|
|
31
|
+
this.getSecretKeys = getSecretKeys;
|
|
32
|
+
this.secretsRequest = secretsRequest;
|
|
33
|
+
}
|
|
34
|
+
async setup(_state) {
|
|
35
|
+
await this.documentStore.save(llm_provider_module_1.LlmContextDocument, {
|
|
36
|
+
role: 'user',
|
|
37
|
+
text: this.render((0, node_path_1.join)(__dirname, 'templates', 'systemMessage.md')),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
async llmTurn(_state) {
|
|
41
|
+
const result = await this.llmGenerateText.call({}, {
|
|
42
|
+
config: {
|
|
43
|
+
provider: 'claude',
|
|
44
|
+
model: 'claude-haiku-4-5-20251001',
|
|
45
|
+
tools: ['get_secret_keys', 'request_secrets_task'],
|
|
46
|
+
system: this.render((0, node_path_1.join)(__dirname, 'templates', 'system.md')),
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
this.assignState({ llmResult: result.data });
|
|
50
|
+
}
|
|
51
|
+
async executeToolCalls(state) {
|
|
52
|
+
const result = await this.llmDelegateToolCalls.call({
|
|
53
|
+
message: state.llmResult.message,
|
|
54
|
+
callback: { transition: 'toolResultReceived' },
|
|
55
|
+
});
|
|
56
|
+
this.assignState({ delegateResult: result.data });
|
|
57
|
+
}
|
|
58
|
+
hasToolCalls(state) {
|
|
59
|
+
return state.llmResult?.message.stopReason === 'tool_use';
|
|
60
|
+
}
|
|
61
|
+
async toolResultReceived(state, input) {
|
|
62
|
+
const result = await this.llmUpdateToolResult.call({
|
|
63
|
+
delegateResult: state.delegateResult,
|
|
64
|
+
completedTool: input,
|
|
65
|
+
});
|
|
66
|
+
this.assignState({ delegateResult: result.data });
|
|
67
|
+
}
|
|
68
|
+
allToolsCompleteTransition(_state) { }
|
|
69
|
+
allToolsComplete(state) {
|
|
70
|
+
return state.delegateResult?.allCompleted ?? false;
|
|
71
|
+
}
|
|
72
|
+
async userMessage(state, input) {
|
|
73
|
+
await this.documentStore.save(llm_provider_module_1.LlmMessageDocument, { role: 'user', text: input.data });
|
|
74
|
+
}
|
|
75
|
+
respond(_state) { }
|
|
76
|
+
};
|
|
77
|
+
exports.AgenticExampleWorkflow = AgenticExampleWorkflow;
|
|
78
|
+
__decorate([
|
|
79
|
+
(0, common_1.Transition)({ to: 'ready' }),
|
|
80
|
+
__metadata("design:type", Function),
|
|
81
|
+
__metadata("design:paramtypes", [Object]),
|
|
82
|
+
__metadata("design:returntype", Promise)
|
|
83
|
+
], AgenticExampleWorkflow.prototype, "setup", null);
|
|
84
|
+
__decorate([
|
|
85
|
+
(0, common_1.Transition)({ from: 'ready', to: 'prompt_executed' }),
|
|
86
|
+
__metadata("design:type", Function),
|
|
87
|
+
__metadata("design:paramtypes", [Object]),
|
|
88
|
+
__metadata("design:returntype", Promise)
|
|
89
|
+
], AgenticExampleWorkflow.prototype, "llmTurn", null);
|
|
90
|
+
__decorate([
|
|
91
|
+
(0, common_1.Transition)({ from: 'prompt_executed', to: 'awaiting_tools', priority: 10 }),
|
|
92
|
+
(0, common_1.Guard)('hasToolCalls'),
|
|
93
|
+
__metadata("design:type", Function),
|
|
94
|
+
__metadata("design:paramtypes", [Object]),
|
|
95
|
+
__metadata("design:returntype", Promise)
|
|
96
|
+
], AgenticExampleWorkflow.prototype, "executeToolCalls", null);
|
|
97
|
+
__decorate([
|
|
98
|
+
(0, common_1.Transition)({ from: 'awaiting_tools', to: 'awaiting_tools', wait: true, schema: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()) }),
|
|
99
|
+
__metadata("design:type", Function),
|
|
100
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
101
|
+
__metadata("design:returntype", Promise)
|
|
102
|
+
], AgenticExampleWorkflow.prototype, "toolResultReceived", null);
|
|
103
|
+
__decorate([
|
|
104
|
+
(0, common_1.Transition)({ from: 'awaiting_tools', to: 'ready' }),
|
|
105
|
+
(0, common_1.Guard)('allToolsComplete'),
|
|
106
|
+
__metadata("design:type", Function),
|
|
107
|
+
__metadata("design:paramtypes", [Object]),
|
|
108
|
+
__metadata("design:returntype", void 0)
|
|
109
|
+
], AgenticExampleWorkflow.prototype, "allToolsCompleteTransition", null);
|
|
110
|
+
__decorate([
|
|
111
|
+
(0, common_1.Transition)({ from: 'waiting_for_user', to: 'ready', wait: true, schema: zod_1.z.string() }),
|
|
112
|
+
__metadata("design:type", Function),
|
|
113
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
114
|
+
__metadata("design:returntype", Promise)
|
|
115
|
+
], AgenticExampleWorkflow.prototype, "userMessage", null);
|
|
116
|
+
__decorate([
|
|
117
|
+
(0, common_1.Transition)({ from: 'prompt_executed', to: 'end' }),
|
|
118
|
+
__metadata("design:type", Function),
|
|
119
|
+
__metadata("design:paramtypes", [Object]),
|
|
120
|
+
__metadata("design:returntype", void 0)
|
|
121
|
+
], AgenticExampleWorkflow.prototype, "respond", null);
|
|
122
|
+
exports.AgenticExampleWorkflow = AgenticExampleWorkflow = __decorate([
|
|
123
|
+
(0, common_1.Workflow)({
|
|
124
|
+
title: 'Secrets - Agentic Example',
|
|
125
|
+
description: 'An agent workflow where the LLM autonomously manages secrets by calling getSecretKeys and requestSecrets tools. The user can send follow-up messages.',
|
|
126
|
+
widget: './agentic-example.ui.yaml',
|
|
127
|
+
}),
|
|
128
|
+
__metadata("design:paramtypes", [llm_provider_module_1.LlmGenerateTextTool,
|
|
129
|
+
llm_provider_module_1.LlmDelegateToolCallsTool,
|
|
130
|
+
llm_provider_module_1.LlmUpdateToolResultTool,
|
|
131
|
+
secrets_module_1.RequestSecretsTask,
|
|
132
|
+
secrets_module_1.GetSecretKeysTool,
|
|
133
|
+
secrets_module_1.SecretsRequestWorkflow])
|
|
134
|
+
], AgenticExampleWorkflow);
|
|
135
|
+
//# sourceMappingURL=agentic-example.workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentic-example.workflow.js","sourceRoot":"","sources":["../../../src/workflows/agentic/agentic-example.workflow.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,yCAAiC;AACjC,6BAAwB;AACxB,8CAA8E;AAG9E,wEAMwC;AACxC,8DAA0G;AAanG,IAAM,sBAAsB,GAA5B,MAAM,sBAAuB,SAAQ,qBAAY;IAEnC;IACA;IACA;IACA;IACA;IACA;IANnB,YACmB,eAAoC,EACpC,oBAA8C,EAC9C,mBAA4C,EAC5C,cAAkC,EAClC,aAAgC,EAChC,cAAsC;QAEvD,KAAK,EAAE,CAAC;QAPS,oBAAe,GAAf,eAAe,CAAqB;QACpC,yBAAoB,GAApB,oBAAoB,CAA0B;QAC9C,wBAAmB,GAAnB,mBAAmB,CAAyB;QAC5C,mBAAc,GAAd,cAAc,CAAoB;QAClC,kBAAa,GAAb,aAAa,CAAmB;QAChC,mBAAc,GAAd,cAAc,CAAwB;IAGzD,CAAC;IAGK,AAAN,KAAK,CAAC,KAAK,CAAC,MAAoB;QAC9B,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,wCAAkB,EAAE;YAChD,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAA,gBAAI,EAAC,SAAS,EAAE,WAAW,EAAE,kBAAkB,CAAC,CAAC;SACpE,CAAC,CAAC;IACL,CAAC;IAGK,AAAN,KAAK,CAAC,OAAO,CAAC,MAAoB;QAChC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAC5C,EAAE,EACF;YACE,MAAM,EAAE;gBACN,QAAQ,EAAE,QAAQ;gBAClB,KAAK,EAAE,2BAA2B;gBAClC,KAAK,EAAE,CAAC,iBAAiB,EAAE,sBAAsB,CAAC;gBAClD,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,IAAA,gBAAI,EAAC,SAAS,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;aAC/D;SACF,CACF,CAAC;QACF,IAAI,CAAC,WAAW,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAIK,AAAN,KAAK,CAAC,gBAAgB,CAAC,KAAmB;QACxC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC;YAClD,OAAO,EAAE,KAAK,CAAC,SAAU,CAAC,OAAO;YACjC,QAAQ,EAAE,EAAE,UAAU,EAAE,oBAAoB,EAAE;SAC/C,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,YAAY,CAAC,KAAmB;QAC9B,OAAO,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,KAAK,UAAU,CAAC;IAC5D,CAAC;IAGK,AAAN,KAAK,CAAC,kBAAkB,CAAC,KAAmB,EAAE,KAA+C;QAC3F,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC;YACjD,cAAc,EAAE,KAAK,CAAC,cAAe;YACrC,aAAa,EAAE,KAAK;SACrB,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;IAID,0BAA0B,CAAC,MAAoB,IAAG,CAAC;IAEnD,gBAAgB,CAAC,KAAmB;QAClC,OAAO,KAAK,CAAC,cAAc,EAAE,YAAY,IAAI,KAAK,CAAC;IACrD,CAAC;IAGK,AAAN,KAAK,CAAC,WAAW,CAAC,KAAmB,EAAE,KAA8B;QACnE,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,wCAAkB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IACxF,CAAC;IAGD,OAAO,CAAC,MAAoB,IAAG,CAAC;CACjC,CAAA;AA1EY,wDAAsB;AAa3B;IADL,IAAA,mBAAU,EAAC,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;;;;mDAM3B;AAGK;IADL,IAAA,mBAAU,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,iBAAiB,EAAE,CAAC;;;;qDAcpD;AAIK;IAFL,IAAA,mBAAU,EAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,EAAE,EAAE,gBAAgB,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC3E,IAAA,cAAK,EAAC,cAAc,CAAC;;;;8DAOrB;AAOK;IADL,IAAA,mBAAU,EAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;;;;gEAOnH;AAID;IAFC,IAAA,mBAAU,EAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;IACnD,IAAA,cAAK,EAAC,kBAAkB,CAAC;;;;wEACyB;AAO7C;IADL,IAAA,mBAAU,EAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,EAAE,CAAC;;;;yDAGrF;AAGD;IADC,IAAA,mBAAU,EAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;;;;qDACnB;iCAzErB,sBAAsB;IANlC,IAAA,iBAAQ,EAAC;QACR,KAAK,EAAE,2BAA2B;QAClC,WAAW,EACT,uJAAuJ;QACzJ,MAAM,EAAE,2BAA2B;KACpC,CAAC;qCAGoC,yCAAmB;QACd,8CAAwB;QACzB,6CAAuB;QAC5B,mCAAkB;QACnB,kCAAiB;QAChB,uCAAsB;GAP9C,sBAAsB,CA0ElC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
You are a helpful assistant that manages workspace secrets.
|
|
2
|
+
Use getSecretKeys to check existing secrets, and requestSecrets to ask the user for new ones.
|
|
3
|
+
|
|
4
|
+
IMPORTANT: When using requestSecrets, it must be the ONLY tool call in your response.
|
|
5
|
+
Do not combine it with other tool calls.
|
|
6
|
+
|
|
7
|
+
When all secrets are available, respond with one sentence including a list of the requested secrets.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BaseWorkflow } from '@loopstack/common';
|
|
2
|
+
import { GetSecretKeysTool, RequestSecretsTool } from '@loopstack/secrets-module';
|
|
3
|
+
interface DeterministicState {
|
|
4
|
+
secretKeys?: Array<{
|
|
5
|
+
key: string;
|
|
6
|
+
hasValue: boolean;
|
|
7
|
+
}>;
|
|
8
|
+
}
|
|
9
|
+
export declare class DeterministicExampleWorkflow extends BaseWorkflow {
|
|
10
|
+
private readonly requestSecrets;
|
|
11
|
+
private readonly getSecretKeys;
|
|
12
|
+
constructor(requestSecrets: RequestSecretsTool, getSecretKeys: GetSecretKeysTool);
|
|
13
|
+
requestSecretsFromUser(_state: DeterministicState): Promise<void>;
|
|
14
|
+
secretsSubmitted(_state: DeterministicState): Promise<void>;
|
|
15
|
+
showResult(state: DeterministicState): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=deterministic-example.workflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deterministic-example.workflow.d.ts","sourceRoot":"","sources":["../../../src/workflows/deterministic/deterministic-example.workflow.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAA0C,MAAM,mBAAmB,CAAC;AACzF,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAyB,MAAM,2BAA2B,CAAC;AAEzG,UAAU,kBAAkB;IAC1B,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACxD;AAED,qBAKa,4BAA6B,SAAQ,YAAY;IAE1D,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa;gBADb,cAAc,EAAE,kBAAkB,EAClC,aAAa,EAAE,iBAAiB;IAM7C,sBAAsB,CAAC,MAAM,EAAE,kBAAkB;IAWjD,gBAAgB,CAAC,MAAM,EAAE,kBAAkB;IAM3C,UAAU,CAAC,KAAK,EAAE,kBAAkB;CAO3C"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.DeterministicExampleWorkflow = void 0;
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
const common_1 = require("@loopstack/common");
|
|
15
|
+
const secrets_module_1 = require("@loopstack/secrets-module");
|
|
16
|
+
let DeterministicExampleWorkflow = class DeterministicExampleWorkflow extends common_1.BaseWorkflow {
|
|
17
|
+
requestSecrets;
|
|
18
|
+
getSecretKeys;
|
|
19
|
+
constructor(requestSecrets, getSecretKeys) {
|
|
20
|
+
super();
|
|
21
|
+
this.requestSecrets = requestSecrets;
|
|
22
|
+
this.getSecretKeys = getSecretKeys;
|
|
23
|
+
}
|
|
24
|
+
async requestSecretsFromUser(_state) {
|
|
25
|
+
await this.requestSecrets.call({
|
|
26
|
+
variables: [{ key: 'EXAMPLE_API_KEY' }, { key: 'EXAMPLE_SECRET' }],
|
|
27
|
+
});
|
|
28
|
+
await this.documentStore.save(secrets_module_1.SecretRequestDocument, {
|
|
29
|
+
variables: [{ key: 'EXAMPLE_API_KEY' }, { key: 'EXAMPLE_SECRET' }],
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async secretsSubmitted(_state) {
|
|
33
|
+
const result = await this.getSecretKeys.call();
|
|
34
|
+
this.assignState({ secretKeys: result.data });
|
|
35
|
+
}
|
|
36
|
+
async showResult(state) {
|
|
37
|
+
await this.documentStore.save(common_1.MarkdownDocument, {
|
|
38
|
+
markdown: this.render((0, node_path_1.join)(__dirname, 'templates', 'secretsVerified.md'), {
|
|
39
|
+
secretKeys: state.secretKeys,
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
exports.DeterministicExampleWorkflow = DeterministicExampleWorkflow;
|
|
45
|
+
__decorate([
|
|
46
|
+
(0, common_1.Transition)({ to: 'requesting_secrets' }),
|
|
47
|
+
__metadata("design:type", Function),
|
|
48
|
+
__metadata("design:paramtypes", [Object]),
|
|
49
|
+
__metadata("design:returntype", Promise)
|
|
50
|
+
], DeterministicExampleWorkflow.prototype, "requestSecretsFromUser", null);
|
|
51
|
+
__decorate([
|
|
52
|
+
(0, common_1.Transition)({ from: 'requesting_secrets', to: 'verifying', wait: true }),
|
|
53
|
+
__metadata("design:type", Function),
|
|
54
|
+
__metadata("design:paramtypes", [Object]),
|
|
55
|
+
__metadata("design:returntype", Promise)
|
|
56
|
+
], DeterministicExampleWorkflow.prototype, "secretsSubmitted", null);
|
|
57
|
+
__decorate([
|
|
58
|
+
(0, common_1.Transition)({ from: 'verifying', to: 'end' }),
|
|
59
|
+
__metadata("design:type", Function),
|
|
60
|
+
__metadata("design:paramtypes", [Object]),
|
|
61
|
+
__metadata("design:returntype", Promise)
|
|
62
|
+
], DeterministicExampleWorkflow.prototype, "showResult", null);
|
|
63
|
+
exports.DeterministicExampleWorkflow = DeterministicExampleWorkflow = __decorate([
|
|
64
|
+
(0, common_1.Workflow)({
|
|
65
|
+
title: 'Secrets - Deterministic Example',
|
|
66
|
+
description: 'A scripted workflow that requests two secrets from the user, waits for them to be stored, then verifies the result. No LLM involved.',
|
|
67
|
+
}),
|
|
68
|
+
__metadata("design:paramtypes", [secrets_module_1.RequestSecretsTool,
|
|
69
|
+
secrets_module_1.GetSecretKeysTool])
|
|
70
|
+
], DeterministicExampleWorkflow);
|
|
71
|
+
//# sourceMappingURL=deterministic-example.workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deterministic-example.workflow.js","sourceRoot":"","sources":["../../../src/workflows/deterministic/deterministic-example.workflow.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,yCAAiC;AACjC,8CAAyF;AACzF,8DAAyG;AAWlG,IAAM,4BAA4B,GAAlC,MAAM,4BAA6B,SAAQ,qBAAY;IAEzC;IACA;IAFnB,YACmB,cAAkC,EAClC,aAAgC;QAEjD,KAAK,EAAE,CAAC;QAHS,mBAAc,GAAd,cAAc,CAAoB;QAClC,kBAAa,GAAb,aAAa,CAAmB;IAGnD,CAAC;IAGK,AAAN,KAAK,CAAC,sBAAsB,CAAC,MAA0B;QACrD,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC;YAC7B,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,iBAAiB,EAAE,EAAE,EAAE,GAAG,EAAE,gBAAgB,EAAE,CAAC;SACnE,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,sCAAqB,EAAE;YACnD,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,iBAAiB,EAAE,EAAE,EAAE,GAAG,EAAE,gBAAgB,EAAE,CAAC;SACnE,CAAC,CAAC;IACL,CAAC;IAGK,AAAN,KAAK,CAAC,gBAAgB,CAAC,MAA0B;QAC/C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;QAC/C,IAAI,CAAC,WAAW,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAGK,AAAN,KAAK,CAAC,UAAU,CAAC,KAAyB;QACxC,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,yBAAgB,EAAE;YAC9C,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,IAAA,gBAAI,EAAC,SAAS,EAAE,WAAW,EAAE,oBAAoB,CAAC,EAAE;gBACxE,UAAU,EAAE,KAAK,CAAC,UAAU;aAC7B,CAAC;SACH,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AAjCY,oEAA4B;AASjC;IADL,IAAA,mBAAU,EAAC,EAAE,EAAE,EAAE,oBAAoB,EAAE,CAAC;;;;0EASxC;AAGK;IADL,IAAA,mBAAU,EAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;;;;oEAIvE;AAGK;IADL,IAAA,mBAAU,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;;;;8DAO5C;uCAhCU,4BAA4B;IALxC,IAAA,iBAAQ,EAAC;QACR,KAAK,EAAE,iCAAiC;QACxC,WAAW,EACT,sIAAsI;KACzI,CAAC;qCAGmC,mCAAkB;QACnB,kCAAiB;GAHxC,4BAA4B,CAiCxC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@loopstack/secrets-examples",
|
|
3
|
+
"displayName": "Loopstack Secrets Examples",
|
|
4
|
+
"description": "Workflow examples for secrets management in Loopstack — deterministic request/verify flow and an agentic LLM flow that calls secrets tools.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"loopstack",
|
|
7
|
+
"example",
|
|
8
|
+
"secrets",
|
|
9
|
+
"workflow"
|
|
10
|
+
],
|
|
11
|
+
"version": "0.1.1",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": {
|
|
14
|
+
"name": "Jakob Klippel",
|
|
15
|
+
"url": "https://www.linkedin.com/in/jakob-klippel/"
|
|
16
|
+
},
|
|
17
|
+
"main": "dist/index.js",
|
|
18
|
+
"types": "dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": "./dist/index.js",
|
|
21
|
+
"./src/*": "./src/*"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "nest build",
|
|
25
|
+
"compile": "tsc --noEmit",
|
|
26
|
+
"format": "prettier --write .",
|
|
27
|
+
"lint": "eslint .",
|
|
28
|
+
"test": "vitest run --passWithNoTests",
|
|
29
|
+
"watch": "nest build --watch"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@loopstack/claude-module": "*",
|
|
33
|
+
"@loopstack/common": "*",
|
|
34
|
+
"@loopstack/llm-provider-module": "*",
|
|
35
|
+
"@loopstack/secrets-module": "*",
|
|
36
|
+
"@nestjs/common": "^11.1.19",
|
|
37
|
+
"zod": "^4.3.6"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"src"
|
|
42
|
+
],
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"vitest": "^4.1.6",
|
|
45
|
+
"@swc/core": "^1.15.33",
|
|
46
|
+
"unplugin-swc": "^1.5.9"
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { ClaudeModule } from '@loopstack/claude-module';
|
|
3
|
+
import { StudioApp } from '@loopstack/common';
|
|
4
|
+
import { SecretsModule } from '@loopstack/secrets-module';
|
|
5
|
+
import { AgenticExampleWorkflow } from './workflows/agentic/agentic-example.workflow';
|
|
6
|
+
import { DeterministicExampleWorkflow } from './workflows/deterministic/deterministic-example.workflow';
|
|
7
|
+
|
|
8
|
+
@StudioApp({
|
|
9
|
+
title: 'Secrets Examples',
|
|
10
|
+
workflows: [DeterministicExampleWorkflow, AgenticExampleWorkflow],
|
|
11
|
+
})
|
|
12
|
+
@Module({
|
|
13
|
+
imports: [ClaudeModule, SecretsModule.forFeature()],
|
|
14
|
+
providers: [DeterministicExampleWorkflow, AgenticExampleWorkflow],
|
|
15
|
+
exports: [DeterministicExampleWorkflow, AgenticExampleWorkflow],
|
|
16
|
+
})
|
|
17
|
+
export class SecretsExamplesModule {}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { BaseWorkflow, Guard, Transition, Workflow } from '@loopstack/common';
|
|
4
|
+
import type { TransitionInput } from '@loopstack/common';
|
|
5
|
+
import type { LlmDelegateResult, LlmGenerateTextResult } from '@loopstack/llm-provider-module';
|
|
6
|
+
import {
|
|
7
|
+
LlmContextDocument,
|
|
8
|
+
LlmDelegateToolCallsTool,
|
|
9
|
+
LlmGenerateTextTool,
|
|
10
|
+
LlmMessageDocument,
|
|
11
|
+
LlmUpdateToolResultTool,
|
|
12
|
+
} from '@loopstack/llm-provider-module';
|
|
13
|
+
import { GetSecretKeysTool, RequestSecretsTask, SecretsRequestWorkflow } from '@loopstack/secrets-module';
|
|
14
|
+
|
|
15
|
+
interface AgenticState {
|
|
16
|
+
llmResult?: LlmGenerateTextResult;
|
|
17
|
+
delegateResult?: LlmDelegateResult;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@Workflow({
|
|
21
|
+
title: 'Secrets - Agentic Example',
|
|
22
|
+
description:
|
|
23
|
+
'An agent workflow where the LLM autonomously manages secrets by calling getSecretKeys and requestSecrets tools. The user can send follow-up messages.',
|
|
24
|
+
widget: './agentic-example.ui.yaml',
|
|
25
|
+
})
|
|
26
|
+
export class AgenticExampleWorkflow extends BaseWorkflow {
|
|
27
|
+
constructor(
|
|
28
|
+
private readonly llmGenerateText: LlmGenerateTextTool,
|
|
29
|
+
private readonly llmDelegateToolCalls: LlmDelegateToolCallsTool,
|
|
30
|
+
private readonly llmUpdateToolResult: LlmUpdateToolResultTool,
|
|
31
|
+
private readonly requestSecrets: RequestSecretsTask,
|
|
32
|
+
private readonly getSecretKeys: GetSecretKeysTool,
|
|
33
|
+
private readonly secretsRequest: SecretsRequestWorkflow,
|
|
34
|
+
) {
|
|
35
|
+
super();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@Transition({ to: 'ready' })
|
|
39
|
+
async setup(_state: AgenticState) {
|
|
40
|
+
await this.documentStore.save(LlmContextDocument, {
|
|
41
|
+
role: 'user',
|
|
42
|
+
text: this.render(join(__dirname, 'templates', 'systemMessage.md')),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Transition({ from: 'ready', to: 'prompt_executed' })
|
|
47
|
+
async llmTurn(_state: AgenticState) {
|
|
48
|
+
const result = await this.llmGenerateText.call(
|
|
49
|
+
{},
|
|
50
|
+
{
|
|
51
|
+
config: {
|
|
52
|
+
provider: 'claude',
|
|
53
|
+
model: 'claude-haiku-4-5-20251001',
|
|
54
|
+
tools: ['get_secret_keys', 'request_secrets_task'],
|
|
55
|
+
system: this.render(join(__dirname, 'templates', 'system.md')),
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
this.assignState({ llmResult: result.data });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@Transition({ from: 'prompt_executed', to: 'awaiting_tools', priority: 10 })
|
|
63
|
+
@Guard('hasToolCalls')
|
|
64
|
+
async executeToolCalls(state: AgenticState) {
|
|
65
|
+
const result = await this.llmDelegateToolCalls.call({
|
|
66
|
+
message: state.llmResult!.message,
|
|
67
|
+
callback: { transition: 'toolResultReceived' },
|
|
68
|
+
});
|
|
69
|
+
this.assignState({ delegateResult: result.data });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
hasToolCalls(state: AgenticState): boolean {
|
|
73
|
+
return state.llmResult?.message.stopReason === 'tool_use';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@Transition({ from: 'awaiting_tools', to: 'awaiting_tools', wait: true, schema: z.record(z.string(), z.unknown()) })
|
|
77
|
+
async toolResultReceived(state: AgenticState, input: TransitionInput<Record<string, unknown>>) {
|
|
78
|
+
const result = await this.llmUpdateToolResult.call({
|
|
79
|
+
delegateResult: state.delegateResult!,
|
|
80
|
+
completedTool: input,
|
|
81
|
+
});
|
|
82
|
+
this.assignState({ delegateResult: result.data });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@Transition({ from: 'awaiting_tools', to: 'ready' })
|
|
86
|
+
@Guard('allToolsComplete')
|
|
87
|
+
allToolsCompleteTransition(_state: AgenticState) {}
|
|
88
|
+
|
|
89
|
+
allToolsComplete(state: AgenticState): boolean {
|
|
90
|
+
return state.delegateResult?.allCompleted ?? false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@Transition({ from: 'waiting_for_user', to: 'ready', wait: true, schema: z.string() })
|
|
94
|
+
async userMessage(state: AgenticState, input: TransitionInput<string>) {
|
|
95
|
+
await this.documentStore.save(LlmMessageDocument, { role: 'user', text: input.data });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@Transition({ from: 'prompt_executed', to: 'end' })
|
|
99
|
+
respond(_state: AgenticState) {}
|
|
100
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
You are a helpful assistant that manages workspace secrets.
|
|
2
|
+
Use getSecretKeys to check existing secrets, and requestSecrets to ask the user for new ones.
|
|
3
|
+
|
|
4
|
+
IMPORTANT: When using requestSecrets, it must be the ONLY tool call in your response.
|
|
5
|
+
Do not combine it with other tool calls.
|
|
6
|
+
|
|
7
|
+
When all secrets are available, respond with one sentence including a list of the requested secrets.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { BaseWorkflow, MarkdownDocument, Transition, Workflow } from '@loopstack/common';
|
|
3
|
+
import { GetSecretKeysTool, RequestSecretsTool, SecretRequestDocument } from '@loopstack/secrets-module';
|
|
4
|
+
|
|
5
|
+
interface DeterministicState {
|
|
6
|
+
secretKeys?: Array<{ key: string; hasValue: boolean }>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
@Workflow({
|
|
10
|
+
title: 'Secrets - Deterministic Example',
|
|
11
|
+
description:
|
|
12
|
+
'A scripted workflow that requests two secrets from the user, waits for them to be stored, then verifies the result. No LLM involved.',
|
|
13
|
+
})
|
|
14
|
+
export class DeterministicExampleWorkflow extends BaseWorkflow {
|
|
15
|
+
constructor(
|
|
16
|
+
private readonly requestSecrets: RequestSecretsTool,
|
|
17
|
+
private readonly getSecretKeys: GetSecretKeysTool,
|
|
18
|
+
) {
|
|
19
|
+
super();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@Transition({ to: 'requesting_secrets' })
|
|
23
|
+
async requestSecretsFromUser(_state: DeterministicState) {
|
|
24
|
+
await this.requestSecrets.call({
|
|
25
|
+
variables: [{ key: 'EXAMPLE_API_KEY' }, { key: 'EXAMPLE_SECRET' }],
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
await this.documentStore.save(SecretRequestDocument, {
|
|
29
|
+
variables: [{ key: 'EXAMPLE_API_KEY' }, { key: 'EXAMPLE_SECRET' }],
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@Transition({ from: 'requesting_secrets', to: 'verifying', wait: true })
|
|
34
|
+
async secretsSubmitted(_state: DeterministicState) {
|
|
35
|
+
const result = await this.getSecretKeys.call();
|
|
36
|
+
this.assignState({ secretKeys: result.data });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@Transition({ from: 'verifying', to: 'end' })
|
|
40
|
+
async showResult(state: DeterministicState) {
|
|
41
|
+
await this.documentStore.save(MarkdownDocument, {
|
|
42
|
+
markdown: this.render(join(__dirname, 'templates', 'secretsVerified.md'), {
|
|
43
|
+
secretKeys: state.secretKeys,
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|