@pikku/core 0.12.148 → 0.12.150

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.
@@ -22,6 +22,16 @@ type StudioScreenBase = {
22
22
  layout?: 'bare';
23
23
  /** Hides the screen and its nav for viewers without access. The addon's functions must enforce the same scopes: hiding a screen is not access control. */
24
24
  scopes?: string[];
25
+ /**
26
+ * The agent that answers in Studio's chat panel while this screen is open.
27
+ * `'studio:<name>'` names an agent Studio ships (e.g. `'studio:knowledge'`);
28
+ * `pikku all` only checks its format. A bare name is an agent the addon ships:
29
+ * it resolves to `agents/<name>.md` in the addon package: the agent's
30
+ * instructions, which name the skills it follows. Its frontmatter has a
31
+ * `title` and a `description`, both message keys in the addon's catalog.
32
+ * Lowercase letters, digits and hyphens, starting with a letter. Leave it out and the chat is unchanged.
33
+ */
34
+ agent?: string;
25
35
  } & ({
26
36
  /** `'studio'` gives `/<path>`, `'team'` gives `/<team>/<path>`. */
27
37
  scope: 'studio' | 'team';
@@ -1,6 +1,6 @@
1
1
  import type { CoreUserSession, GetCredential } from '../../types/core.types.js';
2
2
  import type { CoreAgent, AgentInput, AgentToolDef, AgentMessage, AgentStreamChannel, PikkuAgentMiddlewareHooks, SessionScope } from './agent.types.js';
3
- import type { AgentRunnerParams } from '../../services/agent-runner-service.js';
3
+ import type { AgentRunnerParams, AgentRunnerService } from '../../services/agent-runner-service.js';
4
4
  import { PikkuError } from '../../errors/error-handler.js';
5
5
  import type { SessionService } from '../../services/user-session-service.js';
6
6
  /**
@@ -155,11 +155,18 @@ export declare function buildToolDefs(params: RunAgentParams, agentSessionMap: M
155
155
  tools: AgentToolDef[];
156
156
  missingRpcs: string[];
157
157
  }>;
158
+ /**
159
+ * The singleton `agentRunner`, keyed with the caller's `AI_API_KEY` credential when one is set.
160
+ * Every entry that calls the model (run, stream, approve, resume) goes through this.
161
+ */
162
+ export declare function resolveAgentRunner(singletonServices: {
163
+ agentRunner?: AgentRunnerService;
164
+ }, params: RunAgentParams): Promise<AgentRunnerService>;
158
165
  export declare function prepareAgentRun(agentName: string, input: AgentInput, params: RunAgentParams, agentSessionMap: Map<string, string>, streamContext?: StreamContext): Promise<{
159
166
  agent: CoreAgent;
160
167
  packageName: string | null;
161
168
  resolvedName: string;
162
- agentRunner: import("../../services/agent-runner-service.js").AgentRunnerService;
169
+ agentRunner: AgentRunnerService;
163
170
  storage: import("../../services/agent-storage-service.js").AgentStorageService | undefined;
164
171
  memoryConfig: import("./agent.types.js").AgentMemoryConfig | undefined;
165
172
  threadId: string;
@@ -679,20 +679,28 @@ export async function buildToolDefs(params, agentSessionMap, resourceId, agentNa
679
679
  }
680
680
  return { tools, missingRpcs };
681
681
  }
682
- export async function prepareAgentRun(agentName, input, params, agentSessionMap, streamContext) {
683
- const singletonServices = getSingletonServices();
684
- const { agent, packageName, resolvedName } = resolveAgent(agentName);
685
- await assertAgentAuthorized(agent, params, packageName);
686
- let agentRunner = singletonServices.agentRunner;
682
+ /**
683
+ * The singleton `agentRunner`, keyed with the caller's `AI_API_KEY` credential when one is set.
684
+ * Every entry that calls the model (run, stream, approve, resume) goes through this.
685
+ */
686
+ export async function resolveAgentRunner(singletonServices, params) {
687
+ const agentRunner = singletonServices.agentRunner;
687
688
  if (!agentRunner) {
688
689
  throw new AIProviderNotConfiguredError();
689
690
  }
690
691
  if (params.getCredential && agentRunner.withApiKey) {
691
692
  const aiCredential = await params.getCredential('AI_API_KEY');
692
693
  if (aiCredential?.apiKey?.trim()) {
693
- agentRunner = agentRunner.withApiKey(aiCredential.apiKey);
694
+ return agentRunner.withApiKey(aiCredential.apiKey);
694
695
  }
695
696
  }
697
+ return agentRunner;
698
+ }
699
+ export async function prepareAgentRun(agentName, input, params, agentSessionMap, streamContext) {
700
+ const singletonServices = getSingletonServices();
701
+ const { agent, packageName, resolvedName } = resolveAgent(agentName);
702
+ await assertAgentAuthorized(agent, params, packageName);
703
+ const agentRunner = await resolveAgentRunner(singletonServices, params);
696
704
  const { storage } = resolveMemoryServices(agent, singletonServices);
697
705
  const memoryConfig = agent.memory;
698
706
  const threadId = input.threadId;
@@ -50,7 +50,7 @@ export const createAgentRPC = (wire, options) => {
50
50
  await resumeAgent({ runId, ...input }, wrapChannelWithAGUI(streamingChannel(), { runId }), params(), streamOptions);
51
51
  },
52
52
  interrupt: async (runId, reason) => interruptAgent({ runId, ...(reason ? { reason } : {}) }, { sessionService: options.sessionService }),
53
- approve: async (runId, approvals, expectedAgentName) => asRunResult(await resumeAgentSync(runId, approvals, { sessionService: options.sessionService }, expectedAgentName === undefined
53
+ approve: async (runId, approvals, expectedAgentName) => asRunResult(await resumeAgentSync(runId, approvals, params(), expectedAgentName === undefined
54
54
  ? undefined
55
55
  : mapAddonCall(wire.addonNamespace, expectedAgentName))),
56
56
  };
@@ -1,5 +1,5 @@
1
1
  import { saveMessages, resolveMemoryServices, loadContextMessages, trimMessages, getWorkingMemoryMiddleware, } from './agent-memory.js';
2
- import { prepareAgentRun, resolveAgent, buildInstructions, buildToolDefs, resolveOwnerResourceId, agentSessionScope, assertResourcePrincipalOwner, assertRunBelongsToAgent, assertAgentAuthorized, } from './agent-prepare.js';
2
+ import { prepareAgentRun, resolveAgent, buildInstructions, buildToolDefs, resolveOwnerResourceId, agentSessionScope, assertResourcePrincipalOwner, assertRunBelongsToAgent, assertAgentAuthorized, resolveAgentRunner, } from './agent-prepare.js';
3
3
  import { checkForApprovals, appendStepMessages } from './agent-stream.js';
4
4
  import { AgentInterruptedError, isAbortError, persistOrphanedToolResults, registerInterruptibleRun, trackInterruptNote, trackToolExecution, } from './agent-interrupt.js';
5
5
  import { pikkuState, getSingletonServices } from '../../pikku-state.js';
@@ -7,7 +7,6 @@ import { applyInputMiddleware, describeApprovals, notifyAfterStep, toAccumulated
7
7
  import { applyOutputMiddleware, finalizeAgentRun, lastUserMessageText, } from './agent-finalize.js';
8
8
  import { resolveModelConfig } from './agent-model-config.js';
9
9
  import { validateAgentOutput } from './agent-output.js';
10
- import { AIProviderNotConfiguredError } from '../../errors/errors.js';
11
10
  import { deniedToolResult, randomUUID } from './agent-utils.js';
12
11
  function stripNulls(obj) {
13
12
  if (obj === null)
@@ -308,10 +307,7 @@ export async function resumeAgentSync(runId, approvals, params, expectedAgentNam
308
307
  await assertAgentAuthorized(agent, params, packageName);
309
308
  const { storage } = resolveMemoryServices(agent, singletonServices);
310
309
  const memoryConfig = agent.memory;
311
- const agentRunner = singletonServices.agentRunner;
312
- if (!agentRunner) {
313
- throw new AIProviderNotConfiguredError();
314
- }
310
+ const agentRunner = await resolveAgentRunner(singletonServices, params);
315
311
  const approvedIds = new Set(approvals.filter((a) => a.approved).map((a) => a.toolCallId));
316
312
  const rejectedIds = new Set(approvals.filter((a) => !a.approved).map((a) => a.toolCallId));
317
313
  const savedPendingApprovals = [...(run.pendingApprovals ?? [])];
@@ -1,12 +1,11 @@
1
1
  import { finalizeAgentRun, lastUserMessageText } from './agent-finalize.js';
2
2
  import { pikkuState, getSingletonServices } from '../../pikku-state.js';
3
3
  import { applyInputMiddleware, describeApprovals } from './agent-turn.js';
4
- import { AIProviderNotConfiguredError } from '../../errors/errors.js';
5
4
  import { deniedToolResult, randomUUID } from './agent-utils.js';
6
5
  import { SPOKEN_TRANSCRIPT } from './voice-input.js';
7
6
  import { combineChannelMiddleware, wrapChannelWithMiddleware, } from '../../channel-middleware-runner.js';
8
7
  import { resolveMemoryServices, loadContextMessages, trimMessages, getWorkingMemoryMiddleware, } from './agent-memory.js';
9
- import { prepareAgentRun, resolveAgent, buildInstructions, buildToolDefs, createScopedChannel, resolveOwnerResourceId, agentSessionScope, assertResourceOwner, assertResourcePrincipalOwner, assertAgentAuthorized, ToolApprovalRequired, ToolCredentialRequired, APPROVAL_REQUIRED, CREDENTIAL_REQUIRED, } from './agent-prepare.js';
8
+ import { prepareAgentRun, resolveAgent, buildInstructions, buildToolDefs, createScopedChannel, resolveOwnerResourceId, agentSessionScope, assertResourceOwner, assertResourcePrincipalOwner, assertAgentAuthorized, resolveAgentRunner, ToolApprovalRequired, ToolCredentialRequired, APPROVAL_REQUIRED, CREDENTIAL_REQUIRED, } from './agent-prepare.js';
10
9
  import { resolveModelConfig } from './agent-model-config.js';
11
10
  import { getInFlightTools, isAbortError, persistOrphanedToolResults, registerInterruptibleRun, signalRunInterrupt, trackInterruptNote, trackToolExecution, } from './agent-interrupt.js';
12
11
  function createPersistingChannel(parent, storage, threadId, logger) {
@@ -752,6 +751,7 @@ export async function resumeAgent(input, channel, params, options) {
752
751
  }
753
752
  const { agent, packageName, resolvedName } = resolveAgent(run.agentName);
754
753
  await assertAgentAuthorized(agent, params, packageName);
754
+ const agentRunner = await resolveAgentRunner(singletonServices, params);
755
755
  // The read above is not a claim — concurrent resumes all see the same pending
756
756
  // approval. `resolveApproval` is the claim, and the loser must not go on to
757
757
  // run the tool a second time.
@@ -761,10 +761,6 @@ export async function resumeAgent(input, channel, params, options) {
761
761
  }
762
762
  const { storage } = resolveMemoryServices(agent, singletonServices);
763
763
  const memoryConfig = agent.memory;
764
- const agentRunner = singletonServices.agentRunner;
765
- if (!agentRunner) {
766
- throw new AIProviderNotConfiguredError();
767
- }
768
764
  if (!input.approved) {
769
765
  if (pending.type === 'agent-call') {
770
766
  await agentRunState.updateRun(pending.agentRunId, { status: 'failed' });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/core",
3
- "version": "0.12.148",
3
+ "version": "0.12.150",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/pikkujs/pikku.git",