Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1923,6 +1923,7 @@ export const ERROR_CODES: {
readonly LOUSHO_REGISTRY_UNSAFE_PATH: "Do not install this item; it asks to write outside its allowed folder or is too large, so tell the registry owner.";
readonly LOUSHO_REGISTRY_FILE_EXISTS: "Pass --overwrite to replace the existing file, or move your file away first.";
readonly LOUSHO_REGISTRY_MANIFEST_MISMATCH: "Do not install this item: its code reaches for something its permission manifest does not declare; tell the registry owner, who fixes the code or the manifest.";
readonly LOUSHO_PROMPT_UNRESOLVED: "Start the observe daemon (set LOUSHO_OBSERVE to its URL), or pass `instructions` as a fallback.";
readonly LOUSHO_TOOL_NOT_FOUND: "Use one of the tool names listed in the message, or register the tool yourself.";
readonly LOUSHO_TOOL_NEEDS_CREDENTIALS: "Build the agent with createAgent() and pass the configured tool.";
readonly LOUSHO_TOOL_EXECUTION_FAILED: "Look at the tool's own error (the `cause`) and fix the tool or its input.";
Expand Down
1 change: 1 addition & 0 deletions api/mcp.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,7 @@ const ERROR_CODES: {
readonly LOUSHO_REGISTRY_UNSAFE_PATH: "Do not install this item; it asks to write outside its allowed folder or is too large, so tell the registry owner.";
readonly LOUSHO_REGISTRY_FILE_EXISTS: "Pass --overwrite to replace the existing file, or move your file away first.";
readonly LOUSHO_REGISTRY_MANIFEST_MISMATCH: "Do not install this item: its code reaches for something its permission manifest does not declare; tell the registry owner, who fixes the code or the manifest.";
readonly LOUSHO_PROMPT_UNRESOLVED: "Start the observe daemon (set LOUSHO_OBSERVE to its URL), or pass `instructions` as a fallback.";
readonly LOUSHO_TOOL_NOT_FOUND: "Use one of the tool names listed in the message, or register the tool yourself.";
readonly LOUSHO_TOOL_NEEDS_CREDENTIALS: "Build the agent with createAgent() and pass the configured tool.";
readonly LOUSHO_TOOL_EXECUTION_FAILED: "Look at the tool's own error (the `cause`) and fix the tool or its input.";
Expand Down
1 change: 1 addition & 0 deletions api/testing.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const ERROR_CODES: {
readonly LOUSHO_REGISTRY_UNSAFE_PATH: "Do not install this item; it asks to write outside its allowed folder or is too large, so tell the registry owner.";
readonly LOUSHO_REGISTRY_FILE_EXISTS: "Pass --overwrite to replace the existing file, or move your file away first.";
readonly LOUSHO_REGISTRY_MANIFEST_MISMATCH: "Do not install this item: its code reaches for something its permission manifest does not declare; tell the registry owner, who fixes the code or the manifest.";
readonly LOUSHO_PROMPT_UNRESOLVED: "Start the observe daemon (set LOUSHO_OBSERVE to its URL), or pass `instructions` as a fallback.";
readonly LOUSHO_TOOL_NOT_FOUND: "Use one of the tool names listed in the message, or register the tool yourself.";
readonly LOUSHO_TOOL_NEEDS_CREDENTIALS: "Build the agent with createAgent() and pass the configured tool.";
readonly LOUSHO_TOOL_EXECUTION_FAILED: "Look at the tool's own error (the `cause`) and fix the tool or its input.";
Expand Down
1 change: 1 addition & 0 deletions api/tools.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,7 @@ const ERROR_CODES: {
readonly LOUSHO_REGISTRY_UNSAFE_PATH: "Do not install this item; it asks to write outside its allowed folder or is too large, so tell the registry owner.";
readonly LOUSHO_REGISTRY_FILE_EXISTS: "Pass --overwrite to replace the existing file, or move your file away first.";
readonly LOUSHO_REGISTRY_MANIFEST_MISMATCH: "Do not install this item: its code reaches for something its permission manifest does not declare; tell the registry owner, who fixes the code or the manifest.";
readonly LOUSHO_PROMPT_UNRESOLVED: "Start the observe daemon (set LOUSHO_OBSERVE to its URL), or pass `instructions` as a fallback.";
readonly LOUSHO_TOOL_NOT_FOUND: "Use one of the tool names listed in the message, or register the tool yourself.";
readonly LOUSHO_TOOL_NEEDS_CREDENTIALS: "Build the agent with createAgent() and pass the configured tool.";
readonly LOUSHO_TOOL_EXECUTION_FAILED: "Look at the tool's own error (the `cause`) and fix the tool or its input.";
Expand Down
1 change: 1 addition & 0 deletions api/worker.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,7 @@ const ERROR_CODES: {
readonly LOUSHO_REGISTRY_UNSAFE_PATH: "Do not install this item; it asks to write outside its allowed folder or is too large, so tell the registry owner.";
readonly LOUSHO_REGISTRY_FILE_EXISTS: "Pass --overwrite to replace the existing file, or move your file away first.";
readonly LOUSHO_REGISTRY_MANIFEST_MISMATCH: "Do not install this item: its code reaches for something its permission manifest does not declare; tell the registry owner, who fixes the code or the manifest.";
readonly LOUSHO_PROMPT_UNRESOLVED: "Start the observe daemon (set LOUSHO_OBSERVE to its URL), or pass `instructions` as a fallback.";
readonly LOUSHO_TOOL_NOT_FOUND: "Use one of the tool names listed in the message, or register the tool yourself.";
readonly LOUSHO_TOOL_NEEDS_CREDENTIALS: "Build the agent with createAgent() and pass the configured tool.";
readonly LOUSHO_TOOL_EXECUTION_FAILED: "Look at the tool's own error (the `cause`) and fix the tool or its input.";
Expand Down
18 changes: 17 additions & 1 deletion docs/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Find a code by area:

| Area | Codes | When you meet them |
| ---- | ----- | ------------------ |
| [Configuration](#configuration) | [`LOUSHO_CONFIG_INVALID`](#lousho_config_invalid), [`LOUSHO_CONFIG_MISSING_PROVIDER`](#lousho_config_missing_provider), [`LOUSHO_CONFIG_MISSING_AGENT`](#lousho_config_missing_agent), [`LOUSHO_CONFIG_MISSING_INPUT`](#lousho_config_missing_input), [`LOUSHO_CONFIG_CONFLICTING_OPTIONS`](#lousho_config_conflicting_options), [`LOUSHO_CONFIG_MISSING_CHECKPOINT_STORE`](#lousho_config_missing_checkpoint_store), [`LOUSHO_CONFIG_RESOLVER_FAILED`](#lousho_config_resolver_failed) | A `createAgent()` or executor call that is missing or mixing options, or a config resolver that failed. |
| [Configuration](#configuration) | [`LOUSHO_CONFIG_INVALID`](#lousho_config_invalid), [`LOUSHO_CONFIG_MISSING_PROVIDER`](#lousho_config_missing_provider), [`LOUSHO_CONFIG_MISSING_AGENT`](#lousho_config_missing_agent), [`LOUSHO_CONFIG_MISSING_INPUT`](#lousho_config_missing_input), [`LOUSHO_CONFIG_CONFLICTING_OPTIONS`](#lousho_config_conflicting_options), [`LOUSHO_CONFIG_MISSING_CHECKPOINT_STORE`](#lousho_config_missing_checkpoint_store), [`LOUSHO_CONFIG_RESOLVER_FAILED`](#lousho_config_resolver_failed), [`LOUSHO_PROMPT_UNRESOLVED`](#lousho_prompt_unresolved) | A `createAgent()` or executor call that is missing or mixing options, or a config resolver that failed. |
| [Providers and peers](#providers-and-peers) | [`LOUSHO_PROVIDER_SPEC_INVALID`](#lousho_provider_spec_invalid), [`LOUSHO_PROVIDER_UNKNOWN`](#lousho_provider_unknown), [`LOUSHO_PROVIDER_MISSING_API_KEY`](#lousho_provider_missing_api_key), [`LOUSHO_PROVIDER_REQUEST_FAILED`](#lousho_provider_request_failed), [`LOUSHO_PROVIDER_RATE_LIMITED`](#lousho_provider_rate_limited), [`LOUSHO_PEER_MISSING`](#lousho_peer_missing), [`LOUSHO_HOSTED_TOOL_UNSUPPORTED`](#lousho_hosted_tool_unsupported), [`LOUSHO_UNSUPPORTED_CONTENT`](#lousho_unsupported_content) | Resolving a model string, a missing API key or peer package, a failed or rate-limited model request, a hosted tool the provider cannot run, a file the provider cannot send. |
| [Agent spec files](#agent-spec-files) | [`LOUSHO_SPEC_NOT_FOUND`](#lousho_spec_not_found), [`LOUSHO_SPEC_INVALID`](#lousho_spec_invalid), [`LOUSHO_SPEC_UNKNOWN_FIELD`](#lousho_spec_unknown_field), [`LOUSHO_SPEC_UNSUPPORTED_FORMAT`](#lousho_spec_unsupported_format) | Loading an `AgentSpec` file with `loadSpec()` or `specToAgent()`. |
| [Tools](#tools) | [`LOUSHO_TOOL_NOT_FOUND`](#lousho_tool_not_found), [`LOUSHO_TOOL_NEEDS_CREDENTIALS`](#lousho_tool_needs_credentials), [`LOUSHO_TOOL_EXECUTION_FAILED`](#lousho_tool_execution_failed), [`LOUSHO_TOOL_ARGS_INVALID`](#lousho_tool_args_invalid), [`LOUSHO_MCP_START_FAILED`](#lousho_mcp_start_failed), [`LOUSHO_MCP_CALL_FAILED`](#lousho_mcp_call_failed) | A tool the model called is unknown, needs credentials, threw, or got invalid arguments; a stdio MCP server did not start or a call to one failed. |
Expand Down Expand Up @@ -182,6 +182,22 @@ names the option and `error.cause` is what the function threw.

**Example:** `createAgent({ provider, model: ({ metadata }) => plans[metadata.plan].model })` with an unknown plan.

### LOUSHO_PROMPT_UNRESOLVED

**Means:** `createAgent({ prompt: 'name@version' })` (or `LOUSHO_PROMPT`) has no
`instructions` fallback and the prompt could not be fetched: the observe daemon
is unreachable, answered 404 for that name and version, or `LOUSHO_OBSERVE` is
not set. The first `send()` rejects and `stream()` ends with an `error` event;
the model is never called, so the agent never runs without a system prompt. The
message names the prompt ref.

**Fix:** start the daemon (`lousho observe`) and set `LOUSHO_OBSERVE` to its URL,
publish the prompt (`lousho prompt push`), or pass `instructions` as the fallback
(with it set, an unreachable daemon only logs a warning and the run uses the
embedded text). See [Observability](./observability.md).

**Example:** `createAgent({ provider, prompt: 'mira@latest' })` with no daemon running.

## Providers and peers

### LOUSHO_PROVIDER_SPEC_INVALID
Expand Down
8 changes: 6 additions & 2 deletions docs/observability.md
Original file line number Diff line number Diff line change
Expand Up @@ -417,8 +417,12 @@ The daemon also serves two stores agents can use at runtime:

- **Prompt registry.** `createAgent({ prompt: 'name@version' })` or
`LOUSHO_PROMPT` fetches instructions from it at build (`@latest` resolves
on the daemon; an unreachable daemon falls back to `instructions`). Every
run logs the exact version used. `lousho prompt push` publishes a new
on the daemon; an unreachable daemon falls back to `instructions`; with no
`instructions`, the run fails with `LOUSHO_PROMPT_UNRESOLVED`). A failed
fetch is not cached for good: runs in the next 30 seconds resolve at once
(using `instructions`, or failing), then the next run retries it, so a
daemon that was briefly down at build recovers without a restart. Every run logs the exact version
used. `lousho prompt push` publishes a new
version.
- **Context hub.** `hub()` / `hubTools()` let agents publish and query
namespaced key-values scoped `private` (the writing agent only) or
Expand Down
26 changes: 23 additions & 3 deletions llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8397,7 +8397,7 @@ Find a code by area:

| Area | Codes | When you meet them |
| ---- | ----- | ------------------ |
| [Configuration](#configuration) | [`LOUSHO_CONFIG_INVALID`](#lousho_config_invalid), [`LOUSHO_CONFIG_MISSING_PROVIDER`](#lousho_config_missing_provider), [`LOUSHO_CONFIG_MISSING_AGENT`](#lousho_config_missing_agent), [`LOUSHO_CONFIG_MISSING_INPUT`](#lousho_config_missing_input), [`LOUSHO_CONFIG_CONFLICTING_OPTIONS`](#lousho_config_conflicting_options), [`LOUSHO_CONFIG_MISSING_CHECKPOINT_STORE`](#lousho_config_missing_checkpoint_store), [`LOUSHO_CONFIG_RESOLVER_FAILED`](#lousho_config_resolver_failed) | A `createAgent()` or executor call that is missing or mixing options, or a config resolver that failed. |
| [Configuration](#configuration) | [`LOUSHO_CONFIG_INVALID`](#lousho_config_invalid), [`LOUSHO_CONFIG_MISSING_PROVIDER`](#lousho_config_missing_provider), [`LOUSHO_CONFIG_MISSING_AGENT`](#lousho_config_missing_agent), [`LOUSHO_CONFIG_MISSING_INPUT`](#lousho_config_missing_input), [`LOUSHO_CONFIG_CONFLICTING_OPTIONS`](#lousho_config_conflicting_options), [`LOUSHO_CONFIG_MISSING_CHECKPOINT_STORE`](#lousho_config_missing_checkpoint_store), [`LOUSHO_CONFIG_RESOLVER_FAILED`](#lousho_config_resolver_failed), [`LOUSHO_PROMPT_UNRESOLVED`](#lousho_prompt_unresolved) | A `createAgent()` or executor call that is missing or mixing options, or a config resolver that failed. |
| [Providers and peers](#providers-and-peers) | [`LOUSHO_PROVIDER_SPEC_INVALID`](#lousho_provider_spec_invalid), [`LOUSHO_PROVIDER_UNKNOWN`](#lousho_provider_unknown), [`LOUSHO_PROVIDER_MISSING_API_KEY`](#lousho_provider_missing_api_key), [`LOUSHO_PROVIDER_REQUEST_FAILED`](#lousho_provider_request_failed), [`LOUSHO_PROVIDER_RATE_LIMITED`](#lousho_provider_rate_limited), [`LOUSHO_PEER_MISSING`](#lousho_peer_missing), [`LOUSHO_HOSTED_TOOL_UNSUPPORTED`](#lousho_hosted_tool_unsupported), [`LOUSHO_UNSUPPORTED_CONTENT`](#lousho_unsupported_content) | Resolving a model string, a missing API key or peer package, a failed or rate-limited model request, a hosted tool the provider cannot run, a file the provider cannot send. |
| [Agent spec files](#agent-spec-files) | [`LOUSHO_SPEC_NOT_FOUND`](#lousho_spec_not_found), [`LOUSHO_SPEC_INVALID`](#lousho_spec_invalid), [`LOUSHO_SPEC_UNKNOWN_FIELD`](#lousho_spec_unknown_field), [`LOUSHO_SPEC_UNSUPPORTED_FORMAT`](#lousho_spec_unsupported_format) | Loading an `AgentSpec` file with `loadSpec()` or `specToAgent()`. |
| [Tools](#tools) | [`LOUSHO_TOOL_NOT_FOUND`](#lousho_tool_not_found), [`LOUSHO_TOOL_NEEDS_CREDENTIALS`](#lousho_tool_needs_credentials), [`LOUSHO_TOOL_EXECUTION_FAILED`](#lousho_tool_execution_failed), [`LOUSHO_TOOL_ARGS_INVALID`](#lousho_tool_args_invalid), [`LOUSHO_MCP_START_FAILED`](#lousho_mcp_start_failed), [`LOUSHO_MCP_CALL_FAILED`](#lousho_mcp_call_failed) | A tool the model called is unknown, needs credentials, threw, or got invalid arguments; a stdio MCP server did not start or a call to one failed. |
Expand Down Expand Up @@ -8491,6 +8491,22 @@ names the option and `error.cause` is what the function threw.

**Example:** `createAgent({ provider, model: ({ metadata }) => plans[metadata.plan].model })` with an unknown plan.

### LOUSHO_PROMPT_UNRESOLVED

**Means:** `createAgent({ prompt: 'name@version' })` (or `LOUSHO_PROMPT`) has no
`instructions` fallback and the prompt could not be fetched: the observe daemon
is unreachable, answered 404 for that name and version, or `LOUSHO_OBSERVE` is
not set. The first `send()` rejects and `stream()` ends with an `error` event;
the model is never called, so the agent never runs without a system prompt. The
message names the prompt ref.

**Fix:** start the daemon (`lousho observe`) and set `LOUSHO_OBSERVE` to its URL,
publish the prompt (`lousho prompt push`), or pass `instructions` as the fallback
(with it set, an unreachable daemon only logs a warning and the run uses the
embedded text). See [Observability](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/observability.md).

**Example:** `createAgent({ provider, prompt: 'mira@latest' })` with no daemon running.

## Providers and peers

### LOUSHO_PROVIDER_SPEC_INVALID
Expand Down Expand Up @@ -14083,8 +14099,12 @@ The daemon also serves two stores agents can use at runtime:

- **Prompt registry.** `createAgent({ prompt: 'name@version' })` or
`LOUSHO_PROMPT` fetches instructions from it at build (`@latest` resolves
on the daemon; an unreachable daemon falls back to `instructions`). Every
run logs the exact version used. `lousho prompt push` publishes a new
on the daemon; an unreachable daemon falls back to `instructions`; with no
`instructions`, the run fails with `LOUSHO_PROMPT_UNRESOLVED`). A failed
fetch is not cached for good: runs in the next 30 seconds resolve at once
(using `instructions`, or failing), then the next run retries it, so a
daemon that was briefly down at build recovers without a restart. Every run logs the exact version
used. `lousho prompt push` publishes a new
version.
- **Context hub.** `hub()` / `hubTools()` let agents publish and query
namespaced key-values scoped `private` (the writing agent only) or
Expand Down
3 changes: 2 additions & 1 deletion src/cli/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ const HELP = `${USAGE}

Manages the prompt registry a 'lousho observe' collector serves. Agents run with
createAgent({ prompt: 'name@version' }) or LOUSHO_PROMPT=name@version and fetch their
instructions here at build (falling back to embedded instructions when the daemon is down).
instructions here at build (falling back to embedded instructions when the daemon is down; without
them the run fails with LOUSHO_PROMPT_UNRESOLVED).

Options:
--name N 'push': the registry name (default: the file's basename, e.g. prompts/mira.txt -> mira; required for '-')
Expand Down
36 changes: 29 additions & 7 deletions src/createAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1564,6 +1564,7 @@ async function resolveOption<T>(option: string, value: PerRun<T>, ctx: RunConfig
try {
return await value(ctx);
} catch (error) {
if (error instanceof SDKError && error.code === 'LOUSHO_PROMPT_UNRESOLVED') throw error;
throw new ConfigurationError(
`createAgent: the '${option}' function threw while resolving this run's config: ${error instanceof Error ? error.message : String(error)}`,
option,
Expand Down Expand Up @@ -1776,23 +1777,44 @@ function agentInstructions(config: CreateAgentConfig): AgentInstructions {
}
const env = typeof process !== 'undefined' ? process.env : undefined;
const ref = promptIsRef ? (config.prompt as string) : config.prompt === undefined ? env?.LOUSHO_PROMPT : undefined;
const fetched = ref !== undefined ? registryPrompt(ref, { env }) : undefined;
if (ref !== undefined && fetched === undefined) {
const embedded = promptIsRef ? config.instructions : (config.instructions ?? config.prompt);
if (ref === undefined) return { instructions: embedded, option: config.prompt === undefined ? 'instructions' : 'prompt' };
return registryInstructions(ref, embedded, env, config.prompt === undefined ? 'instructions' : 'prompt');
}

/** The instructions of an agent with a registry `ref`: the fetched text, `embedded` as the fallback, or (without it) a run-time LOUSHO_PROMPT_UNRESOLVED. */
function registryInstructions(ref: string, embedded: PerRun<string | undefined> | undefined, env: NodeJS.ProcessEnv | undefined, option: AgentInstructions['option']): AgentInstructions {
// No embedded fallback: an unresolvable ref fails the run rather than running with no system prompt.
const failClosed = embedded === undefined;
const fetched = registryPrompt(ref, { env, hasFallback: !failClosed });
if (fetched === undefined) {
if (failClosed) return { instructions: () => Promise.reject(promptUnresolved(ref, 'LOUSHO_OBSERVE is not set')), option: 'instructions' };
console.warn(
`[lousho] prompt: '${ref}' needs a collector to resolve - set LOUSHO_OBSERVE=1 (or a daemon URL); the embedded instructions are used instead.`
);
}
const embedded = promptIsRef ? config.instructions : (config.instructions ?? config.prompt);
if (fetched === undefined) {
return { instructions: embedded, option: config.prompt === undefined ? 'instructions' : 'prompt' };
return { instructions: embedded, option };
}
return {
option: 'instructions',
promptUse: fetched.use,
instructions: (ctx) => fetched.text(() => (embedded === undefined ? Promise.resolve(undefined) : resolveOption('instructions', embedded, ctx))),
instructions: (ctx) =>
fetched.text(() =>
embedded === undefined
? Promise.reject(promptUnresolved(ref, 'the observe daemon did not return it'))
: resolveOption('instructions', embedded, ctx)
),
};
}

/** The error a registry prompt with no `instructions` fallback fails the run with. */
function promptUnresolved(ref: string, why: string): SDKError {
return new SDKError(
`Prompt '${ref}' could not be resolved (${why}) and no \`instructions\` fallback is set, so the agent would run with no system prompt. ` +
"Start the observe daemon (set LOUSHO_OBSERVE to its URL) or add `instructions` as a fallback.",
'LOUSHO_PROMPT_UNRESOLVED'
);
}

/** The default `retry` for models given as strings: as many retries as the `ai` SDK makes on its own. */
const DEFAULT_RETRY: WithRetryOptions = { maxRetries: 2 };

Expand Down
Loading
Loading