@lazyingart/agintiflow 0.20.336-integration.2 → 0.20.336-integration.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.336-integration.2",
3
+ "version": "0.20.336-integration.3",
4
4
  "type": "module",
5
5
  "description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
6
6
  "license": "Apache-2.0",
@@ -2128,6 +2128,7 @@ async function executesAndSynthesizesPlot() {
2128
2128
  assert.equal(planner.attestation.priorArtifactsAuthorizeExecution, false);
2129
2129
  assert.equal(planner.attestation.priorArtifactsCountAsCurrentEvidence, false);
2130
2130
  assert.equal(planner.attestation.contextualTurnsRequireCurrentExecutionAuthority, true);
2131
+ assert.equal(planner.attestation.hostNativeToolRequestsFailClosed, true);
2131
2132
  assert.equal(planner.attestation.maximumToolCalls, INTEGRATION_ANALYSIS_MAX_TOOL_CALLS);
2132
2133
  assert.equal(planner.attestation.maximumRequiredToolFormationRetries, 2);
2133
2134
  assert.equal(planner.attestation.boundedCurrentRunArtifactEvidence, true);
@@ -2189,6 +2190,105 @@ async function directAnswerDoesNotExecute() {
2189
2190
  coordinator.close();
2190
2191
  }
2191
2192
 
2193
+ async function hostNativeToolRequestsFailClosedWithoutPythonSubstitution() {
2194
+ const prompts = [
2195
+ "Use tmux_list_sessions to inspect active native tmux sessions and tell me what is running.",
2196
+ "Inspect the host terminal sessions and report the active tmux windows.",
2197
+ ];
2198
+ for (let index = 0; index < prompts.length; index += 1) {
2199
+ const prompt = prompts[index];
2200
+ let modelCalls = 0;
2201
+ const progress = [];
2202
+ const finals = [];
2203
+ const routed = fixture(async () => {
2204
+ modelCalls += 1;
2205
+ return toolResponse("print('invented host evidence')");
2206
+ });
2207
+ const result = await routed.planner.run(
2208
+ scope(`run_00000000-0000-4000-8000-${String(170 + index).padStart(12, "0")}`),
2209
+ { prompt },
2210
+ {
2211
+ onProgress(value) {
2212
+ progress.push(value);
2213
+ },
2214
+ onFinal(value) {
2215
+ finals.push(value);
2216
+ },
2217
+ }
2218
+ );
2219
+ assert.equal(modelCalls, 0, prompt);
2220
+ assert.equal(result.kind, "direct", prompt);
2221
+ assert.equal(result.toolCalls, 0, prompt);
2222
+ assert.equal(result.executionStatus, null, prompt);
2223
+ assert.deepEqual(result.artifacts, [], prompt);
2224
+ assert.match(result.text, /native host\/session tools are not exposed/u, prompt);
2225
+ assert.match(result.text, /No host evidence was collected/u, prompt);
2226
+ assert.doesNotMatch(result.text, /invented|tmux windows:/iu, prompt);
2227
+ assert.deepEqual(finals, [result], prompt);
2228
+ assert.deepEqual(progress, [
2229
+ Object.freeze({
2230
+ phase: "synthesizing",
2231
+ toolCallsCompleted: 0,
2232
+ executionSucceeded: false,
2233
+ artifactCount: 0,
2234
+ }),
2235
+ ], prompt);
2236
+ assert.equal(
2237
+ routed.rpcCalls.filter(({ pathname }) => pathname === EXECUTION_WORKER_RPC_PATHS.jobsStart).length,
2238
+ 0,
2239
+ prompt
2240
+ );
2241
+ routed.coordinator.close();
2242
+ }
2243
+
2244
+ let explanatoryModelCalls = 0;
2245
+ const explanatory = fixture(async () => {
2246
+ explanatoryModelCalls += 1;
2247
+ return textResponse("tmux_list_sessions would list terminal multiplexer sessions when such a host tool exists.");
2248
+ });
2249
+ const explanatoryResult = await explanatory.planner.run(
2250
+ scope("run_00000000-0000-4000-8000-000000000172"),
2251
+ { prompt: "Explain what a tool named tmux_list_sessions would do, without using it." }
2252
+ );
2253
+ assert.equal(explanatoryModelCalls, 1);
2254
+ assert.equal(explanatoryResult.kind, "direct");
2255
+ assert.equal(explanatoryResult.toolCalls, 0);
2256
+ assert.doesNotMatch(explanatoryResult.text, /Capability limit:/u);
2257
+ assert.equal(
2258
+ explanatory.rpcCalls.filter(({ pathname }) => pathname === EXECUTION_WORKER_RPC_PATHS.jobsStart).length,
2259
+ 0
2260
+ );
2261
+ explanatory.coordinator.close();
2262
+
2263
+ const benignPrompts = [
2264
+ "Use train_test_split to split this dataset.",
2265
+ "Show the process values in this supplied table.",
2266
+ "Use psnr_metric to evaluate this reconstruction.",
2267
+ ];
2268
+ for (let index = 0; index < benignPrompts.length; index += 1) {
2269
+ const prompt = benignPrompts[index];
2270
+ let benignModelCalls = 0;
2271
+ const benign = fixture(async () => {
2272
+ benignModelCalls += 1;
2273
+ return textResponse("This benign request is handled without host-native capability claims.");
2274
+ });
2275
+ const benignResult = await benign.planner.run(
2276
+ scope(`run_00000000-0000-4000-8000-${String(173 + index).padStart(12, "0")}`),
2277
+ { prompt }
2278
+ );
2279
+ assert.equal(benignModelCalls, 1, prompt);
2280
+ assert.equal(benignResult.kind, "direct", prompt);
2281
+ assert.equal(benignResult.toolCalls, 0, prompt);
2282
+ assert.doesNotMatch(benignResult.text, /Capability limit:/u, prompt);
2283
+ assert.equal(
2284
+ benign.rpcCalls.filter(({ pathname }) => pathname === EXECUTION_WORKER_RPC_PATHS.jobsStart).length,
2285
+ 0,
2286
+ prompt
2287
+ );
2288
+ benign.coordinator.close();
2289
+ }
2290
+ }
2291
+
2192
2292
  async function unsupportedMixedActionsDiscloseAndContinue() {
2193
2293
  const cases = [
2194
2294
  ["Install numpy, then explain a median in one sentence.", /package installation is unavailable/u],
@@ -5320,6 +5420,7 @@ await groundsWithPrivateSearchBeforeModelSynthesis();
5320
5420
  await deepResearchCompletesWithoutSecondModelSynthesis();
5321
5421
  await executesAndSynthesizesPlot();
5322
5422
  await directAnswerDoesNotExecute();
5423
+ await hostNativeToolRequestsFailClosedWithoutPythonSubstitution();
5323
5424
  await unsupportedMixedActionsDiscloseAndContinue();
5324
5425
  await coordinatedExecutionClausesHonorLocalNegation();
5325
5426
  await leadingGeneralFileImperativesRequireTheFileWorker();
@@ -617,6 +617,74 @@ async function plotThenProseContinuationRoundTrip(temporaryRoot) {
617
617
  }
618
618
  }
619
619
 
620
+ async function hostNativeToolCapabilityLimitRoundTrip(temporaryRoot) {
621
+ const root = path.join(temporaryRoot, "host-native-limit-state");
622
+ const prompt = "Use tmux_list_sessions to inspect active native tmux sessions and tell me what is running.";
623
+ const fixture = createExplicitPythonRunnerFixture({
624
+ async complete() {
625
+ return {
626
+ choices: [{
627
+ message: {
628
+ role: "assistant",
629
+ content: null,
630
+ tool_calls: [{
631
+ id: "call_wrong_host_substitute",
632
+ type: "function",
633
+ function: {
634
+ name: "execute_python_analysis",
635
+ arguments: JSON.stringify({ source: "print('invented host evidence')" }),
636
+ },
637
+ }],
638
+ },
639
+ }],
640
+ };
641
+ },
642
+ });
643
+ let service = createTestOnlyIntegrationAnalysisSessionService({
644
+ analysisRunner: fixture.planner,
645
+ stateRoot: root,
646
+ });
647
+ let restarted = null;
648
+ try {
649
+ const created = await service.createThread({ title: "Host-native limit" }, context());
650
+ const started = await service.startRun({
651
+ threadId: created.thread.id,
652
+ input: { text: prompt },
653
+ }, context());
654
+ await service.waitForIdle();
655
+
656
+ const completed = (await service.getRunStatus({ runId: started.run.id }, context())).run;
657
+ assert.equal(completed.status, "completed", JSON.stringify(completed.error));
658
+ assert.match(completed.output, /native host\/session tools are not exposed/u);
659
+ assert.match(completed.output, /No host evidence was collected/u);
660
+ assert.equal(fixture.modelCalls, 0);
661
+ assert.equal(fixture.workerSources.length, 0);
662
+ assert.equal(
663
+ fixture.rpcCalls.filter(({ pathname }) => pathname === EXECUTION_WORKER_RPC_PATHS.jobsStart).length,
664
+ 0
665
+ );
666
+ const events = await (await service.loadRunEvents(eventsRequest(started.run.id), context()))
667
+ .publicEventLedger.loadEventsAfter(0);
668
+ assert.equal(events.some((event) => event.type.startsWith("tool.")), false);
669
+ assert(events.some((event) => event.type === "run.completed"));
670
+ assert.deepEqual((await service.listArtifacts({ runId: started.run.id }, context())).artifacts, []);
671
+
672
+ await service.close({ mode: "wait" });
673
+ service = null;
674
+ restarted = createTestOnlyIntegrationAnalysisSessionService({
675
+ analysisRunner: fixture.planner,
676
+ stateRoot: root,
677
+ });
678
+ const replayed = (await restarted.getRunStatus({ runId: started.run.id }, context())).run;
679
+ assert.equal(replayed.output, completed.output);
680
+ assert.equal(fixture.modelCalls, 0, "durable replay must not call the model");
681
+ } finally {
682
+ await service?.close({ mode: "abort" }).catch(() => {});
683
+ await restarted?.close({ mode: "abort" }).catch(() => {});
684
+ fixture.coordinator.close();
685
+ }
686
+ }
687
+
620
688
  async function markdownArtifactFollowupUsesToolRoundTrip(temporaryRoot) {
621
689
  const root = path.join(temporaryRoot, "markdown-followup-state");
622
690
  const followupPrompt =
@@ -3998,6 +4066,7 @@ async function main() {
3998
4066
  try {
3999
4067
  await explicitPythonDurabilityRoundTrip(temporaryRoot);
4000
4068
  await plotThenProseContinuationRoundTrip(temporaryRoot);
4069
+ await hostNativeToolCapabilityLimitRoundTrip(temporaryRoot);
4001
4070
  await markdownArtifactFollowupUsesToolRoundTrip(temporaryRoot);
4002
4071
  await boundedPriorArtifactContextRoundTrip(temporaryRoot);
4003
4072
  await groundedSearchDurabilityRoundTrip(temporaryRoot);
@@ -248,7 +248,11 @@ const UNSUPPORTED_SINGLE_TEX_PDF_ACTION =
248
248
  /^(?:make|create|generate|produce|prepare|export|save|download|upload|provide|return|output)\b[^.!?;\r\n]{0,180}(?:\.(?:tex|pdf)\b|\b(?:pdf\s+(?:file|report|document)|(?:latex|tex)\s+(?:source|file)\s+only|(?:as|in)\s+(?:a\s+)?pdf)\b)|^(?:compile|typeset|render|export|build|convert)\b[^.!?;\r\n]{0,180}\b(?:latex|tex|source)\b[^.!?;\r\n]{0,120}\b(?:to|into|as)\s+(?:a\s+)?pdf\b/iu;
249
249
  const UNSUPPORTED_EXTERNAL_ACTION =
250
250
  /^(?:deploy|publish|push|upload|email|post|submit)\b|^send\b[^.!?;\r\n]{0,160}\b(?:email|notification)\b|^send\b[^.!?;\r\n]{0,160}\bto\s+(?!(?:me|us|here|this\s+chat)\b)\S|^(?:change|update|delete|remove)\b[^.!?;\r\n]{0,160}\b(?:account|website|site|server|deployment|repository|repo|setting|record|remote)\b|\b(?:and|then|also)\s+(?:deploy|publish|push|upload|email|post|submit)\b/iu;
251
- const UNSUPPORTED_CAPABILITY_ORDER = Object.freeze(["package", "shell", "search", "web", "file", "external"]);
251
+ const UNSUPPORTED_NATIVE_HOST_TOOL_ACTION =
252
+ /\b(?:use|call|invoke|run|execute)\s+(?:the\s+)?(?!execute_python_analysis\b|grounded_search\b|deep_research\b|compile_tex_document\b|publish_file_artifact\b)(?:(?:tmux|terminal|shell|systemd|journalctl|lsof|netstat|host|native|workstation|ps)(?:_[a-z0-9]+)+|[a-z][a-z0-9]*(?:_[a-z0-9]+)*(?:_tmux|_terminal|_shell|_systemd|_journalctl|_lsof|_netstat|_host|_native|_workstation|_pid|_pids|_processes)(?:_[a-z0-9]+)*)\b/iu;
253
+ const UNSUPPORTED_NATIVE_HOST_INSPECTION_ACTION =
254
+ /\b(?:inspect|list|show|check|query|get|read)\b[^.!?;\r\n]{0,160}\b(?:native|host|workstation)\b[^.!?;\r\n]{0,120}\b(?:tmux|terminal\s+sessions?|shell\s+sessions?|process(?:es)?|pids?|systemd|journalctl|ss|lsof|netstat|ports?|listeners?)\b|\b(?:inspect|list|show|check|query|get|read)\b[^.!?;\r\n]{0,160}\b(?:tmux|terminal\s+sessions?|shell\s+sessions?|systemd|journalctl|ss|lsof|netstat)\b|\b(?:tmux|terminal|shell)\s+sessions?\b[^.!?;\r\n]{0,120}\b(?:inspect|list|show|check|query|get|read|running|active)\b|\b(?:inspect|list|show|check|query|get|read)\b[^.!?;\r\n]{0,120}\b(?:running|host|system)\s+process(?:es)?\b/iu;
255
+ const UNSUPPORTED_CAPABILITY_ORDER = Object.freeze(["package", "shell", "search", "web", "file", "external", "nativeHost"]);
252
256
  const GENERAL_FILE_CREATION_ACTION =
253
257
  /^(?:(?:using|from|with|based\s+on)\b[^.!?;\r\n]{0,180},?\s+)?(?:make|create|generate|write|produce|prepare|export|save|provide|return|output)\b[^.!?;\r\n]{0,220}(?:\b(?:files?|attachments?|downloads?|archives?)\b|\.(?:csv|json|md|markdown|txt|log|xml|html?|css|m?js|cjs|tsx?|py|sh|svg|png|jpe?g|webp|gif|zip|gz|bin|dat|docx|xlsx|pptx)\b)|^turn\b[^.!?;\r\n]{0,160}\binto\b[^.!?;\r\n]{0,100}(?:\b(?:files?|attachments?|downloads?|archives?)\b|\.(?:csv|json|md|markdown|txt|log|xml|html?|css|m?js|cjs|tsx?|py|sh|svg|png|jpe?g|webp|gif|zip|gz|bin|dat|docx|xlsx|pptx)\b)|\b(?:and|then|also)\s+(?:make|create|generate|write|produce|prepare|export|save)\b[^.!?;\r\n]{0,180}(?:\bfiles?\b|\.(?:csv|json|md|txt|py|js|ts|html|svg|png|jpe?g|webp|zip)\b)/iu;
254
258
  const UNSUPPORTED_CAPABILITY_TEXT = Object.freeze({
@@ -258,6 +262,8 @@ const UNSUPPORTED_CAPABILITY_TEXT = Object.freeze({
258
262
  web: "Capability limit: arbitrary web browsing and exact URL opening or fetching are unavailable; enabled Search can retrieve only bounded evidence sources.",
259
263
  file: "Capability limit: the requested file operation is outside the bounded verified local artifact broker.",
260
264
  external: "Capability limit: external actions such as deployment, publishing, uploads, messaging, and email are unavailable.",
265
+ nativeHost:
266
+ "Capability limit: native host/session tools are not exposed in this public Agent; I cannot inspect tmux sessions, host processes, terminals, or private workstation state.",
261
267
  });
262
268
  const EXPRESSION_PLOT_MODEL_FALLBACK_PROMPT =
263
269
  "The user explicitly requires a plot, but the fixed single-expression compiler could not represent this natural-language request. Interpret the request and its conversation context, then call the bounded analysis tool and emit a real plot artifact. Never claim a plot exists unless the tool succeeds and emits it.";
@@ -713,6 +719,16 @@ function requiresGeneralFileCreation(value, { texPdfEnabled = false } = {}) {
713
719
  ));
714
720
  }
715
721
 
722
+ function unsupportedNativeHostCapabilityRequired(value) {
723
+ return unquotedImperativeClauses(value).some((clause) => (
724
+ !UNSUPPORTED_ACTION_EXCLUSION.test(clause) &&
725
+ !NON_EXECUTION_LEAD.test(clause) &&
726
+ !UNSUPPORTED_DISCUSSION_LEAD.test(clause) &&
727
+ (UNSUPPORTED_NATIVE_HOST_TOOL_ACTION.test(clause) ||
728
+ UNSUPPORTED_NATIVE_HOST_INSPECTION_ACTION.test(clause))
729
+ ));
730
+ }
731
+
716
732
  function unsupportedCapabilityRequests(
717
733
  value,
718
734
  { searchEnabled = false, texPdfEnabled = false, fileCreationEnabled = false } = {}
@@ -743,6 +759,10 @@ function unsupportedCapabilityRequests(
743
759
  if (!fileCreationEnabled && UNSUPPORTED_FILE_ACTION.test(fileClause)) requested.add("file");
744
760
  if (!texPdfEnabled && UNSUPPORTED_SINGLE_TEX_PDF_ACTION.test(clause)) requested.add("file");
745
761
  if (UNSUPPORTED_EXTERNAL_ACTION.test(clause)) requested.add("external");
762
+ if (UNSUPPORTED_NATIVE_HOST_TOOL_ACTION.test(clause) ||
763
+ UNSUPPORTED_NATIVE_HOST_INSPECTION_ACTION.test(clause)) {
764
+ requested.add("nativeHost");
765
+ }
746
766
  }
747
767
  return Object.freeze(UNSUPPORTED_CAPABILITY_ORDER.filter((category) => requested.has(category)));
748
768
  }
@@ -752,6 +772,12 @@ function prependCapabilityLimits(text, categories) {
752
772
  return `${categories.map((category) => UNSUPPORTED_CAPABILITY_TEXT[category]).join("\n")}\n\n${text}`;
753
773
  }
754
774
 
775
+ function blockingUnsupportedCapabilityLimitText(categories) {
776
+ const ordered = categories.includes("nativeHost") ? categories : Object.freeze([...categories, "nativeHost"]);
777
+ return `${ordered.map((category) => UNSUPPORTED_CAPABILITY_TEXT[category]).join("\n")}\n\n` +
778
+ "No host evidence was collected, and no bounded Python analysis was run.";
779
+ }
780
+
755
781
  function contextualNoToolTurn(conversation, priorArtifacts, explicitExecution) {
756
782
  return !explicitExecution && (conversation.length > 0 || priorArtifacts.length > 0);
757
783
  }
@@ -2827,6 +2853,7 @@ function createPlanner({
2827
2853
  priorArtifactsAuthorizeExecution: false,
2828
2854
  priorArtifactsCountAsCurrentEvidence: false,
2829
2855
  contextualTurnsRequireCurrentExecutionAuthority: true,
2856
+ hostNativeToolRequestsFailClosed: true,
2830
2857
  maximumToolCalls: INTEGRATION_ANALYSIS_MAX_TOOL_CALLS,
2831
2858
  maximumRequiredToolFormationRetries: MAXIMUM_REQUIRED_TOOL_FORMATION_RETRIES,
2832
2859
  exactToolArguments: true,
@@ -3149,6 +3176,7 @@ function createPlanner({
3149
3176
  texPdfEnabled: documentArtifactIntent.required,
3150
3177
  fileCreationEnabled: fileArtifactRequired,
3151
3178
  });
3179
+ const blockingUnsupportedNativeHost = unsupportedNativeHostCapabilityRequired(input.prompt);
3152
3180
  if (documentArtifactRevision && options.priorDocument === undefined) {
3153
3181
  fail(
3154
3182
  "ANALYSIS_DOCUMENT_SOURCE_REQUIRED",
@@ -3275,6 +3303,26 @@ function createPlanner({
3275
3303
  return finalResult;
3276
3304
  };
3277
3305
 
3306
+ const finalizeBlockingUnsupportedCapability = async () => {
3307
+ const finalResult = publicFinalResult({
3308
+ text: blockingUnsupportedCapabilityLimitText(unsupportedCapabilities),
3309
+ toolCalls: 0,
3310
+ artifacts,
3311
+ executionStatus: null,
3312
+ });
3313
+ if (options.onFinal) await options.onFinal(finalResult);
3314
+ assertNotAborted(signal);
3315
+ return finalResult;
3316
+ };
3317
+
3318
+ if (blockingUnsupportedNativeHost) {
3319
+ await emitProgress("synthesizing", {
3320
+ executionSucceeded: false,
3321
+ artifactCount: 0,
3322
+ });
3323
+ return await finalizeBlockingUnsupportedCapability();
3324
+ }
3325
+
3278
3326
  const executeOnce = async (executionInput, toolCallNumber) => {
3279
3327
  let lastExecutionState = "";
3280
3328
  let terminalSuccessPending = false;