Back to skills
SKILL.md
Continuum Temporal
ASecurityBuild durable agent workflows with Temporal — sequential/parallel/loop/conditional steps, human-in-the-loop approval gates, custom workflows and activities. Invoke when the user asks "long-running workflow", "approval gate", "human in the loop", "retry on failure", "workflow survives restart", or anything Temporal-related.
- 85 stars
- 0 votes
- 0 copies
- 0 views
- Added June 4, 2026
Works with
Security analysis
96/100- Installs packages at runtime which could introduce malicious dependencies
npx -y skills add shyftlabs/continuum --skill continuum-temporal --agent claude-codeAre you the author of Continuum Temporal?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/shyftlabs-continuum-temporal)---
name: continuum-temporal
description: Build durable agent workflows with Temporal — sequential/parallel/loop/conditional steps, human-in-the-loop approval gates, custom workflows and activities. Invoke when the user asks "long-running workflow", "approval gate", "human in the loop", "retry on failure", "workflow survives restart", or anything Temporal-related.
---
# Continuum Temporal Skill
Authoritative sources: [`docs/temporal/getting-started.md`](../../../docs/temporal/getting-started.md),
[`workflow-patterns.md`](../../../docs/temporal/workflow-patterns.md),
[`custom-agents.md`](../../../docs/temporal/custom-agents.md),
[`custom-workflows.md`](../../../docs/temporal/custom-workflows.md),
[`human-in-loop.md`](../../../docs/temporal/human-in-loop.md),
[`docker.md`](../../../docs/temporal/docker.md).
---
## Setup
```bash
pip install "shyftlabs-continuum[temporal]"
docker compose --profile temporal up -d
```
```env
TEMPORAL_ENABLED=true
TEMPORAL_HOST=localhost:7233
TEMPORAL_NAMESPACE=default
TEMPORAL_TASK_QUEUE=orchestrator-agents
TEMPORAL_ENABLE_HUMAN_IN_LOOP=true
```
---
## Three actors
1. **Worker** — long-running process; polls task queue, executes activities.
2. **Workflow** — declarative durable plan; lives inside Temporal.
3. **Caller** — your app; submits workflows, signals/queries them.
---
## Imports
```python
from orchestrator.temporal import (
get_temporal_client, get_worker_manager, get_agent_registry,
WorkflowInput, WorkflowResult,
AgentStep, ApprovalStep, ParallelStep, ConditionalStep, WaitStep,
AgentWorkflow, SequentialAgentWorkflow,
ParallelAgentWorkflow, LoopAgentWorkflow,
ApprovalDecision, ApprovalRequest,
HumanInLoopManager, ApprovalNotificationConfig, NotificationParams,
)
```
---
## Hello-Temporal
```python
async def main():
registry = get_agent_registry()
registry.register(BaseAgent(name="summarizer", instructions="...", model="gpt-4o-mini"))
client = get_temporal_client()
await client.connect()
await get_worker_manager(client, registry).start() # blocks
handle = await client.run_agent_workflow(
WorkflowInput(
steps=[
{"type": "agent", "agent_name": "summarizer"},
{"type": "approval", "description": "Review", "approvers": ["alice"]},
{"type": "agent", "agent_name": "summarizer"},
],
initial_input="...",
),
id="hello-001",
)
result = await handle.result()
print(result.status, result.content)
```
---
## Step types
| `type` | Schema |
|---|---|
| `"agent"` | `{agent_name, input?, timeout=300, retries=3, metadata}` |
| `"approval"` | `{description, approvers=[], timeout=86400}` |
| `"parallel"` | `{agents: [AgentStep…], merge_strategy: "concatenate"\|"first_success"\|"structured"}` |
| `"conditional"` | `{condition_agent, if_true: [...], if_false: [...]}` |
| `"wait"` | `{duration_seconds: 1..604800}` |
`parse_step(dict) -> WorkflowStep` validates a step dict; raises
`ValueError` on unknown `type`.
---
## Convenience workflows
```python
# Sequential
input_data = SequentialWorkflowInput(
agent_names=["a", "b", "c"], initial_input="...",
approval_between_steps=False,
)
await client.start_workflow(SequentialAgentWorkflow.run, input_data)
# Parallel
input_data = ParallelWorkflowInput(
agent_names=["a", "b", "c"], initial_input="...",
merge_strategy="structured",
)
# Loop
input_data = LoopWorkflowInput(
agent_name="reviser", initial_input="...",
max_iterations=10, termination_phrase="COMPLETE",
)
```
---
## Signals & queries on `AgentWorkflow`
| Signal | |
|---|---|
| `submit_approval(decision: ApprovalDecision)` | Approve / reject / escalate |
| `cancel_workflow()` | Cancel |
| `inject_input(data: dict)` | Inject data mid-flow |
| Query | Returns |
|---|---|
| `get_status()` | `{status, current_step_index, total_steps, completed_steps, cancelled}` |
| `get_pending_approvals()` | `list[dict]` |
---
## Human-in-the-loop
```python
async def notify(params: NotificationParams) -> None:
if params.type == "approval_required":
await send_email(...)
get_agent_registry().set_notification_handler(notify)
hlm = HumanInLoopManager(
client,
ApprovalNotificationConfig(
handler=notify,
timeout_seconds=86400,
escalation_timeout=7200,
escalation_handler=on_escalate,
),
)
await hlm.approve("workflow-id", request_id="...", decided_by="alice")
await hlm.reject("workflow-id", request_id="...", decided_by="alice", reason="bad draft")
await hlm.escalate("workflow-id", request_id="...", escalate_to="bob")
pending = await hlm.get_pending_approvals("workflow-id")
status = await hlm.get_workflow_status("workflow-id")
```
---
## Custom workflow + activity
```python
from datetime import timedelta
from temporalio import workflow, activity
from temporalio.common import RetryPolicy
@activity.defn(name="fetch_url_activity")
async def fetch_url_activity(url: str) -> str:
async with httpx.AsyncClient(timeout=30.0) as c:
return (await c.get(url)).text
@workflow.defn(sandboxed=False)
class FetchAndSummarizeWorkflow:
@workflow.run
async def run(self, url: str) -> WorkflowResult:
text = await workflow.execute_activity(
fetch_url_activity, url,
start_to_close_timeout=timedelta(seconds=60),
retry_policy=RetryPolicy(maximum_attempts=3),
)
result = await workflow.execute_activity(
"run_agent_activity",
AgentActivityParams(agent_name="summarizer", input=text),
start_to_close_timeout=timedelta(seconds=120),
)
return WorkflowResult(status="completed", content=result.content)
worker = get_worker_manager(client, registry)
worker.register_workflow(FetchAndSummarizeWorkflow)
worker.register_activity(fetch_url_activity)
await worker.start()
```
---
## Determinism rules (critical!)
In a `@workflow.defn` body:
- ❌ `random.random()`, `uuid.uuid4()`, `datetime.now()`, `time.time()`
- ❌ `requests.get()`, file/DB I/O, third-party SDKs
- ✅ `workflow.uuid4()`, `workflow.random()`, `workflow.now()`
- ✅ `workflow.execute_activity(...)` for I/O
- ✅ `workflow.sleep(...)`, `workflow.wait_condition(...)`
- ✅ `with workflow.unsafe.imports_passed_through():` for type-only imports
---
## Don't
- Don't run I/O in workflow code — push it into an activity.
- Don't reuse a workflow `id` — `WorkflowAlreadyStartedError`.
- Don't forget to register agents with the registry before submitting a
workflow that references them — `AgentNotRegisteredError`.
- Don't forget `await client.connect()` before `start_workflow(...)`.
- Don't run the worker as a fire-and-forget — `await worker.start()`
blocks; in production it's its own process.
Attribution
Comments
Loading comments…